forked from haskell-game/tiny-games-hs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mini-othello.hs
193 lines (154 loc) · 5.47 KB
/
mini-othello.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
main=do{let{infixr 1?;(b?x)y|b=x|0<3=y;k=(<*>)=<<((,)<$>);(&)=map;f=foldl;o=1:2:
o;r=[0..7];c=(length.).filter.(==);s=show&r;l(x,y)=8*y+x;(w^i)b=take i b++w:drop
(i+1)b;u=(putStrLn.).f(++);t=y.splitAt 8;y([],_)_=u" "s;y(a,b)k=u(k!!0)[(n!!)&a]
>>t b(drop 1k);n="_XO";a%c=f(\a d->v a a c d 0)(a,0)(k[-1..1]);v k(a@(b,w),c)p@(
x,y)q@(m,n)s=let{j x=x>=0&&x<8;r=j x&&j y;t=l p;e=b!!t;g=v k((w^t$b,w),c+1)(x+m,
y+n)q}in s==0?(e==0?g 1$k)$s==1?(r&&e==o!!w?g 2$k)$not r||e==0?k$e==w?(a,c+1)$g
2;q a d|((b,w),c)<-a%d=(c==0?b$(w^l d$b),o!!w);z h a@(b,w)=print(n!!w,(`c`b)&[0
..2])>>t b s>>(w/=h?z h(e a)$getLine>>=z h.q a.read);i=fst;j=snd;g h a=f(\c e->i
e>i c?e$c)(-65*h,(0,0))$(\((b,p),d)->(h*(h*p==0?j a`c`i b$i$g(div h(-2))b),d))&(
((,)=<<(a%))&k r);e a=q a.j$g 4a};z 1(1^35$1^28$2^27$2^36$replicate 64 0,1)}
-- ^10 ------------------------------------------------------------------ 80> --
{- gam-10-80-hs-prelude/mini-othello (hellwolf), ghc 9.4.2
https://hackage.haskell.org/package/base/docs/Prelude.html
= Description
This is a minimum Othello implementation with MiniMax "AI" opponent using GHC 9.X
with only prelude.
(Taken from its [wikipedia](https://en.wikipedia.org/wiki/Reversi) entry.)
About Othello(Reversi): Reversi is a strategy board game for two players, played
on an 8×8 uncheckered board. It was invented in 1883. Othello, a variant with a
fixed initial setup of the board, was patented in 1971.
There are sixty-four identical game pieces called disks, which are light on one
side and dark on the other. Players take turns placing disks on the board with
their assigned color facing up. During a play, any disks of the opponent's color
that are in a straight line and bounded by the disk just placed and another disk
of the current player's color are turned over to the current player's color. The
objective of the game is to have the majority of disks turned to display one's
color when the last playable empty square is filled.
= How To Play
== Game Setup
1) By default, AI will play the white side. You may change this by altering the
first argument to which @z@ function is invoked.
2) The game is equipped with a AI with MiniMax strategy. You may also alter game
difficulties by tuning @g 4a@ to @g 8a@, @g 16a@, etc. If you wish to write
your own, change @e@ function.
3) There is no error handling in general, any unexpected input may crash the
program or result in unexpected state.
4) The program is tested with runghc-9.4.2.
== Initial Screen
@
("X",[60,2,2])
0________
1________
2________
3___OX___
4___XO___
5________
6________
7________
01234567
@
1) The first line @("W",[60,2,2])@ is "game status line":
* Whose turn? The game always starts from the black side, where 'X' means
black side's turn, 'O' means white side's turn.
* How many empty spots left? It starts with 60.
* How many black pieces on the board? 2.
* How many white pieces on the board? 2.
2) The last line is for values of the x-axis.
3) The rest is the printout of the board:
* The first column is for values of the "y-axis".
* The rest are the cells with pieces: "X" means a black piece, "O" means a
white piece, and "_" means empty cells.
== Make A Move
1) You are expected to type a input conforming to @read::(Integer,Integer)@ as the
coordinate where you wish to put your piece to.
2) For instance, you make a move at @(3,2)@ which then should progress the game to
next turn:
@
(3,2)
("O",[59,4,1])
0________
1________
2___X____
3___XX___
4___XO___
5________
6________
7________
01234567
@
3) If you make an invalid move, it is considered to yield the turn. You may be
forced to do so in some cases to move the game forward.
4) AI should make a move next, and then it should be back to you:
@
("X",[58,3,3])
0________
1________
2__OX____
3___OX___
4___XO___
5________
6________
7________
01234567
@
== Game Ending
You should keep making moves until you cannot.
1) The game status line should indicate who has the most pieces on the board.
2) AI may run out of moves, and it may simply crash the program :/.
3) The game won't announce the winner automatically, don't be upset :).
An example ending:
@
("O",[1,53,10])
0XXOXXXXX
1XXXOXXXX
2XXXXXXXX
3XXXXOXXX
4XOOXXOXX
5XOOXXXOO
6XXXXXXX0
7XXXXXXXX
01234567
@
= Technical Notes
@
-- | Game state type alias
type GameState = (Board, Side)
-- | Check number of flips for one single direction
v :: GameState -> (GameState, Int) -> Coordinate -> StepCoordinate -> State
-> (GameState, Int)
v savedGameState (currentGameState, nFlips) cor stepCor state
-> (newGameState, nFlips)
-- | Trial play
(%) :: GameState -> Coordinate -> (GameState, Int)
(%) inGameState -> cor -> (outGameState, nFlips)
-- | Play and move to next round
q :: GameState -> Coordinate -> GameState
q currentGameState -> cor -> nextRoundGameState
-- | Start game
z :: Side -> GameState -> IO ()
z humanSide (initialBoard,currentSide)
-- | Strategy function!
e :: GameState -> GameState
This generates a sequence of [(nFlips, (x,y)), ...]
((,)=<<snd.(a%))&k r
-- | Naive strategy
e a=q a.snd.head.f((>0).fst).m((,)=<<snd.(a%)$k r
-------------------------------------------------
-- | Greedy strategy
e a=q a.snd.f(\c@(w,_)e@(d,_)->d>w?e$c)(0,(0,0)).m((,)=<<snd.(a%))$k r
-----------------------------------------------------------------------
-- | MiniMax strategy
i=fst;j=snd;
g h a=f(\c e->i e>i c?e$c)(-65*h,(0,0))$
(\((b,p),d)->(h*(h*p==0?j a`c`i b$i$g(div h(-2))b),d))&
(((,)=<<(a%))&k r);
e a=q a.j$g 4a
-- Other optimizable statements?
@
-}
{-
Local Variables:
fill-column: 80
End:
-}