assocMultiset a := matcher | [] as () with | [] -> [()] | _ -> [] | #$x :: $ as assocMultiset a with | $tgt -> match tgt as list (a, integer) with | $hs ++ (#x, #1) :: $ts -> hs ++ ts | $hs ++ (#x, $n) :: $ts -> hs ++ (x, n - 1) :: ts | $ :: $ as (a, assocMultiset a) with | $tgt -> matchAll tgt as list (a, integer) with | $hs ++ ($x, #1) :: $ts -> (x, hs ++ ts) | $hs ++ ($x, !#1 & $n) :: $ts -> (x, hs ++ (x, n - 1) :: ts) | $ as something with | $tgt -> [tgt] assertEqual "assocMultiset" (matchAll [(1,2),(2,1),(3,3)] as assocMultiset integer with | #1 :: $rs -> rs) [(1, 1), (2, 1), (3, 3)] assertEqual "assocMultiset" (matchAll [(1,2),(2,1),(3,3)] as assocMultiset integer with | #2 :: $rs -> rs) [(1, 2), (3, 3)] assertEqual "assocMultiset" (matchAll [(1,2),(2,1),(3,3)] as assocMultiset integer with | $x :: $rs -> (x,rs)) [(2, [(1, 2), (3, 3)]), (1, [(1, 1), (2, 1), (3, 3)]), (3, [(1, 2), (2, 1), (3, 2)])] assocToList xs := concat (matchAllDFS xs as list (something, integer) with | _ ++ ($x, $n) :: _ -> take n (repeat1 x)) assertEqual "assocToList" (assocToList [(1,2),(2,1),(3,3)]) [1, 1, 2, 3, 3, 3] N := 5 tree a := matcher | node $ $ as (a, multiset (tree a)) with | Node $x $ts -> [(x, ts)] | $ as something with | $tgt -> [tgt] state := (integer, assocMultiset integer, assocMultiset integer) fOrS s := match s as state with | ($h, _, _) -> h transformState s := match s as state with | ($h, $x, $y) -> (h, assocToList x, assocToList y) move s := matchAllDFS s as state with -- equal or less than N | (#1, ($s1 & $x :: _), (?(\y -> x + y < N + 1) & $y) :: $rs') -> (2, s1, add (x + y) rs') -- the single hand becomes more than N | (#1, ($s1 & $x :: _), ?(\y -> x + y > N) :: []) -> (-1, s1, []) -- a hand becomes more than N | (#1, ($s1 & $x :: _), (?(\y -> x + y > N) & $y) :: (![] & $rs')) -> (2, s1, rs') -- equal or less than N | (#2, $x :: $rs', (?(\y -> x + y < N + 1) & $y) :: _ & $s2) -> (1, add (x + y) rs', s2) -- the single hand becomes more than N | (#2, $x :: [], (?(\y -> x + y > N) :: _) & $s2) -> (-2, [], s2) -- a hand becomes more than N | (#2, $x :: (![] & $rs'), (?(\y -> x + y > N) :: _) & $s2) -> (1, rs', s2) add x xs := matchDFS xs as list (integer, integer) with | $hs ++ (#x, $n) :: $ts -> hs ++ (x, n + 1) :: ts | $hs ++ (!((?(\y -> x > y), _) :: _) & $ts) -> hs ++ (x, 1) :: ts | _ -> (x, 1) :: xs "move" move (1, [(2,1)], [(1,1), (5,1)]) -- [(2, [(2, 1)], [(3, 1), (5, 1)]), (2, [(2, 1)], [(1, 1)])] move (2, [(1,1), (5,1)], [(2,1)]) -- [(1, [(3, 1), (5, 1)], [(2, 1)])] assertEqual "add" (add 1 [(1,3),(3,1)]) [(1, 4), (3, 1)] assertEqual "add" (add 2 [(1,3),(3,1)]) [(1, 3), (2, 1), (3, 1)] init := (1, [(1,2)], [(1,2)]) assertEqual "move" (move init) [(2, [(1, 2)], [(1, 1), (2, 1)])] makeTree x := Node x (map makeTree (move x)) assertEqual "makeTree" (makeTree (1, [(2, 1)], [(1, 1)])) (Node (1, [(2, 1)], [(1, 1)]) [Node (2, [(2, 1)], [(3, 1)]) [Node (1, [(5, 1)], [(3, 1)]) [Node (-1, [(5, 1)], []) []]]]) topTree s n := matchAllDFS makeTree s as tree state with | loop $i (1, [1..n], $m) (node $x_i (... :: _)) _ -> map (\i -> x_i) [1..m] paths := matchAllDFS makeTree init as tree state with | loop $i (1, $n) (node $x_i (... :: _)) (node $x_(n + 1) []) -> map (\i -> x_i) [1..(n + 1)] --io (each (compose show print) (head paths)) winningRec s := matchAll makeTree s as tree state with | (node ($h, _, _) ((node ($t & ((#(neg h), _, _) | ?(\t -> (empty? (winningRec t))))) _) :: _)) -> t winningNot s := matchAllDFS makeTree s as tree state with | node ($h, _, _) (loop $i (1, [1..], _) (node $t !(node _ !... :: _) :: _) (node (#(neg h), _, _) _ :: _)) -> t winning c := matchAllDFS makeTree c as tree state with | node ($h, _, _) (loop $i (1, $n) (node $f_i (forall (@ :: _) (node $s_i ...)) :: _) (node ((#(neg h), _, _) & $l) _ :: _)) -> c :: concat (map (\i -> [f_i, s_i]) [1..n]) ++ [l] "winning (first)" io (each (compose (\l -> (map transformState l)) (compose show print)) (winning init)) "winning (second)" winning (2, [(1, 2)], [(2, 1), (1, 1)]) "winning" winning (1, [(5, 2)], [(5, 1)]) "winning" winning (1, [(2, 1)], [(1, 1)]) assertEqual "winningNot (first)" (winningNot init) [(2, [(1, 2)], [(2, 1), (1, 1)])] assertEqual "winningNot (second)" (winningNot (2, [(1, 2)], [(2, 1), (1, 1)])) []