Thanks for checking things out! Here are a couple of things that you might be particularly interested in:
- The final report containing all of the AI testing restults
- A design diagram describing the high-level AI architecture
- The fully commented AI code (written in Haskell)
An interactive game can be started by via stack exec dominos
Should I replace pure
with return
?
Danger, No Danger 6 & 5; -2 : (5130,4870) 6 & 5; -1 : (5106,4894) 6 & 5; 20 : (47958,52042) 6 & 5; -1 : (50383,49617) 6 & 5; -2 : (50588,49412) 6 & 5; -3 : (50588,49412) 6 & 5; -4 : (50387,49613) 6 & 5; -5 : (50371,49629) 6 ; -2 : (50234,49766) 6,5,4; -2 : (50422,49578)
*Main AI DomsMatch Game Control.Parallel.Strategies> foldr1 (\(a,b) (x,y) -> (a+x, b+y)) $ parMap rdeepseq (domsMatch playerHFE playerHFEBM 6250) [1..16]
(48173,51827)
*Main AI DomsMatch Game Control.Parallel.Strategies> foldr1 (\(a,b) (x,y) -> (a+x, b+y)) $ parMap rdeepseq (domsMatch playerHFEBM playerHFE 6250) [1..16]
(51683,48317)
*Main AI DomsMatch Game Control.Parallel.Strategies> foldr1 (\(a,b) (x,y) -> (a+x, b+y)) $ parMap rdeepseq (domsMatch playerHFEB playerHFE 6250) [1..16]
(50588,49412)
*Main AI DomsMatch Game Control.Parallel.Strategies> foldr1 (\(a,b) (x,y) -> (a+x, b+y)) $ parMap rdeepseq (domsMatch playerHFE playerHFEB 6250) [1..16]
(49340,50660)
*Main AI DomsMatch Game Control.Parallel.Strategies> foldr1 (\(a,b) (x,y) -> (a+x, b+y)) $ parMap rdeepseq (domsMatch playerHFEBM playerHFEB 6250) [1..16]
(51059,48941)
*Main AI DomsMatch Game Control.Parallel.Strategies> foldr1 (\(a,b) (x,y) -> (a+x, b+y)) $ parMap rdeepseq (domsMatch playerHFEB playerHFEBM 6250) [1..16]
(48839,51161)
flowchart TB
playerX(playerX :: DomsPlayer\n\n Defined by a list of Tactics)
playerX-- "[Tactic]" -->strategy
strategy-. DomsPlayer .-> playerX
strategy(strategy\n\n Composes individual tactics into\nan overall strategy, returning an\n intelligent `DomsPlayer`)
mergePlays(mergePlays\n\n Merges two play lists, summing\n the points of duplicate plays)
strategy-- "[(Play, Points)]\n[(Play, Points)]" --> mergePlays
mergePlays-. "[(Play, Points)]" .->strategy
allPlays(allPlays\n\n Return all possible plays)
strategy-- "DominoBoard\nHand" -->allPlays
allPlays-. "[Play]" .->strategy
strategy -->H
H-.->strategy
H(highScoring :: Tactic\n\n Zip each play with the\n points it would score)
scorePlay(scorePlay\n\n Simulate a play and\n calculate its score)
H-- "Play\nDominoBoard" -->scorePlay
scorePlay-. Points .->H
strategy -->F
F-.->strategy
F("firstDrop :: Tactic\n\n Encourage dropping (5,4)\n on the first play")
strategy -->E
E-.->strategy
E("endGame :: Tactic\n\n Check if it's possible to either\nwin the game or get to 59")
scoreN(scoreN)
E -- "DominoBoard\nPoints" --> scoreN
scoreN -. "[Play]" .->E
strategy -->B
B-.->strategy
B("blindDanger :: Tactic\n\n Discourage dropping dangerous\n dominos if they can't be knocked off")
strategy -->M
M-.->strategy
M("mostPips :: Tactic\n\n Encourage plays that expose\n pips the player controls")
countPips(countPips\n\n Convert a list of dominos\n into a count of their pips)
M-- "[Domino]" -->countPips
countPips-. "[(Int, Pip)]" .->M
newEnds(newEnds\n\n Simulate a play and return\n the new ends of the board)
M-- "DominoBoard\nPlay" -->newEnds
newEnds-. "[Pip]" .->M
strategy -->S
S-.->strategy
S("smartDanger :: Tactic\n\n Given what the opponent\n could be holding, calculate\n the average risk of each play")
S-->allPlays
allPlays-.->S
allPlays-- "Hand\nBoard" -->possPlays(possPlays)
possPlays-. "([Domino], [Domino])" .->allPlays
S--GameState-->otherHand
otherHand-."Hand".-> S
otherHand(otherHand\n\n A list of dominos that could\n be in the opponent's hand)
knockingPips(knockingPips\n\n Builds a list of pips that\n the opponent has knocked on)
otherHand--History\nPlayer-->knockingPips
knockingPips-."[Pip]".-> otherHand
S--GameState-->otherHandSize
otherHandSize-."Int".-> S
otherHandSize(otherHandSize\n\n Calculate the number of dominos\n the opponent is holding)
flowchart TB
documentPlayer("documentPlayer :: DomsPlayer -> DomsPlayer\n\n A 'wrapper' player that takes an existing AI\n player and prints some information about\n its plays to the screen (so the human knows\n which moves have been played)")
human("human :: DomsPlayer\n\n A `DomsPlayer` controlled by a human\n user. It prints information about the state\n of the game, then interactively asks for\n a move to play")
showGameState("showGameState\n\n Prints the score of the current player as\n well as the score of their opponent, and\n represents the Domino board in a\n user-friendly way")
human-- "DominoBoard\nPlayer\nScores" --> showGameState
showGameState-. "IO ()" .-> human
getPlay("getPlay\n\n Prints the player's current hand, with each\n domino given an index. It then reads two lines\n of input: the index of the domino to play,\n and the end (L or R) to play it on. The two\n inputs are returned as a tuple of `String`s")
human-- "Hand\nDominoBoard" --> getPlay
getPlay-. "IO (String, String)" .-> human
validateChoice("validateChoice\n\n Performs a number of sanity-checks on the\n provided user-input. Checks that the index\n is an `Int`, the end is a proper `End` (L or R),\n that the index isn't too small or too big, and\n that the requested play is actually legal. Returns\n either the fully validated play or an error message")
human-- "(String, String)\nHand\nDominoBoard" --> validateChoice
validateChoice-. "Either String (Domino, End)" .-> human