module Mastermind.Model where
open import FRP.JS.Nat using (ℕ ; zero ; suc ; +_ ; _+_ ; _∸_ ; _*_)
open import FRP.JS.Bool using (Bool ; true ; false ; _∧_ ; if_then_else_ ; not)
renaming (_≟_ to _≟b_)
open import FRP.JS.List using (List ; _∷_ ; [] ; map ; _++_ ; length)
open import Mastermind.Auxiliary
codeLength = 4
maxGuesses = 12
colorCount = 6
data Match : Set where
black white none : Match
data End : Set where
won lost : End
data GameState : Set where
active : GameState
ended : End → GameState
data Direction : Set where
⟵ ⟶ : Direction
Color : Set
Color = ℕ
Colors : Set
Colors = Vec Color codeLength
Matches : Set
Matches = List Match
HistElem : Set
HistElem = Colors × Matches
History : Set
History = List HistElem
_%_ : ℕ -> ℕ -> ℕ
n % m = 0
{-# COMPILED_JS _%_ function(x) { return function(y) { if (x < 0) { return (x % y) + x; } else { return x % y; } }; } #-}
module Random (seed : ℕ) where
private
a = 16807
m = 2147483647
c = 0
next : (n : ℕ) → ℕ
next n = (((a * n) + c) % m)
gen-n : (n : ℕ) → ℕ → Vec ℕ n
gen-n zero s = []
gen-n (suc i) s = val ∷ gen-n i val where
val = next s
step-n : ℕ → ℕ
step-n 0 = seed
step-n (suc n) = next (step-n n)
inc : ℕ
inc = step-n 1
newSolution : Colors × ℕ
newSolution = mapv (λ x → (x % colorCount) + 1) (gen-n 4 seed) , step-n 4
record State : Set where
field
guess : Colors
solution : Colors
history : History
gamestate : GameState
rand : ℕ
guesspos : Fin.Fin 4
private
defGuess : Colors
defGuess = 1 ∷ 1 ∷ 1 ∷ 1 ∷ []
init : ℕ → State
init t with Random.newSolution t
... | solution , seed =
record
{ guess = defGuess
; history = []
; gamestate = active
; solution = solution
; rand = seed
; guesspos = Fin.zero
}
incStateSeed : State → State
incStateSeed s = record s { rand = Random.inc (State.rand s) }
moveGuessPos : Direction → State → State
moveGuessPos ⟶ s =
record s { guesspos = (Fin.inc (State.guesspos s)) }
moveGuessPos ⟵ s =
record s { guesspos = Fin.pred (State.guesspos s) }
modifyColor : Color → State → State
modifyColor c s = incStateSeed (moveGuessPos ⟶ res) where
open State s
res : State
res = record s { guess = replacev (Fin.toℕ guesspos) guess c }
clearGuess : State → State
clearGuess s =
incStateSeed record s
{ guess = defGuess
; guesspos = Fin.zero }
newGame : State → State
newGame s =
init (Random.inc (State.rand s))
_≟m_ : Match → Match → Bool
black ≟m black = true
white ≟m white = true
_ ≟m _ = false
isSolved : Matches → Bool
isSolved g
= all (_≟m_ black) g ∧ (length g ≟ codeLength)
check : Colors → Colors → Matches
check guess solution =
blacks ++ whites
where
zipped = toList (zipv guess solution)
diffs = unzip (filter (not ∘ uncurry _≟_) zipped)
rg = proj₁ diffs
rs = proj₂ diffs
whitec = length (rg \\ (rg \\ rs by _≟_) by _≟_)
whites = replicate whitec white
same = filter (uncurry _≟_) zipped
blacks = map (const black) same
checkGuess : State → State
checkGuess s =
incStateSeed record s
{ history = newhist
; gamestate = newstate
; guesspos = Fin.zero
} where
open State s
matched = check guess solution
newhist = history ++ (guess , matched) ∷ []
newstate =
if isSolved matched then
ended won
else (
if length newhist < maxGuesses then
active
else
ended lost
)