-
Notifications
You must be signed in to change notification settings - Fork 0
/
Nim.hs
131 lines (98 loc) · 3.57 KB
/
Nim.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
{-# LANGUAGE ScopedTypeVariables, LambdaCase, GeneralizedNewtypeDeriving #-}
module Nim where {
import Data.List;
import Control.Monad;
import Control.Exception(assert);
import Debug.Trace;
import Retrograde;
import Data.Map(Map);
import qualified Data.Tuple as Tuple;
import qualified Data.Map as Map;
-- to avoid the redundancy warning
trace_placeholder :: ();
trace_placeholder = trace "trace" $ assert False ();
max_piles :: Piles;
max_piles = Piles 8;
max_coins :: Coins;
max_coins = Coins 7;
newtype Piles = Piles Integer deriving (Show, Ord, Eq);
newtype Coins = Coins Integer deriving (Show, Ord, Eq, Enum);
unCoins :: Coins -> Integer;
unCoins (Coins i)=i;
type Position = [Coins];
uniq :: (Ord a, Eq a) => [a] -> [a];
uniq = map head . group . sort;
final_entries :: [(Position,Value)];
final_entries = [([],loss)];
retrograde_positions :: Position -> [Position];
retrograde_positions p = assert (is_canonical p) $ canonicalize $ new_pile p ++ add_to_some_pile p;
new_pile :: Position -> [Position];
new_pile l = if Piles (genericLength l) < max_piles then do {
new <- enumFromTo (Coins 1) max_coins;
return $ new:l
} else [];
is_canonical :: Position -> Bool;
is_canonical [] = True;
is_canonical l@(Coins n:_) = if n<1 then False
else l==sort l;
canonicalize1 :: Position -> Position;
canonicalize1 = dropWhile (\n -> n == Coins 0) . sort;
canonicalize :: [Position] -> [Position];
canonicalize = uniq . map canonicalize1;
add_to_some_pile :: Position -> [Position];
add_to_some_pile = modify_some_pile $ \h -> enumFromTo (succ h) max_coins;
successors :: Position -> [Position];
successors = canonicalize . (modify_some_pile $ \h -> enumFromTo (Coins 0) (pred h));
modify_some_pile :: (Coins -> [Coins]) -> Position -> [Position];
modify_some_pile f l = assert (is_canonical l) $ do {
(p,q) <- zip (inits l) (tails l);
guard $ not $ null q;
let {h = head q};
newh <- f h;
return $ p ++ (newh:tail q);
};
value_via_successors :: Position -> [Entry] -> Maybe Value;
value_via_successors l succs = let {
table :: Map Position Value;
table = Map.fromList succs;
} in combine_values_greedy $ map ((flip Map.lookup) table) $ successors l;
type Entry = (Position, Value);
mapfn :: Entry -> [(Position, Epoch)];
mapfn (pos, _val) = (pos,Known):(map (\x -> (x, Unknown)) $ retrograde_positions pos);
redfn :: Position -> [(Entry, Epoch)] -> [Entry];
redfn pos esuccs = if any (\case {(_,Known) -> True; _->False}) esuccs
then []
else case value_via_successors pos (map fst esuccs) of {
Nothing -> [];
Just v -> [(pos,v)];
};
do_mapreduce :: [Entry] -> [Entry];
do_mapreduce = mapReduce mapfn redfn;
iterate_mapreduce :: [Entry] -> [[Entry]];
iterate_mapreduce start = let {
more = do_mapreduce start;
} in if null more then []
else more:iterate_mapreduce (start ++ more);
binary :: Integer -> [Bool];
binary = map (\x -> x==1) . (unfoldr $ \v -> if v==0 then Nothing else Just $ Tuple.swap $ divMod v 2);
bin_add :: [Bool] -> [Bool] -> [Bool];
bin_add x [] = x;
bin_add [] x = x;
bin_add (p:p2) (q:q2) = xor p q : bin_add p2 q2;
xor :: Bool -> Bool -> Bool;
xor = (/=);
nim_bsum :: [[Bool]] -> [Bool];
nim_bsum = foldl' bin_add [];
nim_sum :: [Coins] -> [Bool];
nim_sum = nim_bsum . map (binary . unCoins);
is_zero :: [Bool] -> Bool;
is_zero = not . any id;
test_item :: Position -> Value -> Bool;
test_item p v = (is_zero $ nim_sum p) == (v < draw);
all_answers :: [Entry];
all_answers = concat $ iterate_mapreduce final_entries;
all_test :: (Int,Bool);
all_test = (length all_answers, and $ map (uncurry test_item) all_answers);
lookup_answer :: Map Position Value;
lookup_answer = Map.fromList all_answers;
} --end