1votos

Tic tac toe en Haskell

por josejuan hace 4 años

Puede calcularse todo el árbol de jugadas directamente y luego usar simplemente el peor caso, el mejor caso u otro cualquiera.

Crear una aplicacion que simule el juego tic tac toe.

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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
{-# LANGUAGE RecordWildCards, BangPatterns #-} 
import Data.List 
import Data.Function 
 
-- Un jugador puede ser el A (humano) o el B (computadora) 
data Player = A | B deriving (Eq, Show, Read) 
 
-- Una celda del tablero puede contener un jugador o nada 
data Cell = Player Player | N deriving (Eq) 
 
-- ¿Como se muestran las celdas? 
instance Show Cell where 
  show (Player p) = " " ++ show p ++ " " 
  show         N  = " · " 
 
-- El estado del tablero son las celdas 
newtype State = State [Cell] 
 
-- Tablero vacío 
emptyState :: State 
emptyState = State [N,N,N,N,N,N,N,N,N] 
 
-- Marcar un jugador (hacer un movimiento) 
move :: Player -> State -> Int -> State 
move p (State cs) i = State $ take (i - 1) cs ++ [Player p] ++ drop i cs 
 
-- ¿Como se muestra un tablero? 
instance Show State where 
  show (State [a, b, c, d, e, f, g, h, i]) = unlines [s [a,b,c], r, s [d, e, f], r, s [g, h, i]] 
                                             where s = concat . intersperse "|" . map show 
                                                   r = "---+---+---" 
 
-- Resultado (para la computadora) de elegir cierto movimiento 
data Result = Win | Loss | Draw deriving (Eq, Show) 
 
-- Decimos que el resultado está ordenado porque la máquina primero prefiere ganar ella y por último tú 
instance Ord Result where 
  Win  `compare` Loss = LT 
  Win  `compare` Draw = LT 
  Draw `compare` Loss = LT 
  x    `compare` y    = if x == y then EQ else GT 
 
-- Estadísticas en casa paso 
data Step = Step { result :: !Result    -- El peor resultado para la máquina si se sigue por ahí 
                 , move2  :: !Int       -- El movimiento que se realiza 
                 , inner  :: ![Step]    -- Los posibles movimientos del contrario 
                 } deriving (Show) 
 
-- Según quien seleccione, el camino será bueno o malo 
combineSubResults :: Player -> [Result] -> Result 
combineSubResults A = last . sort 
combineSubResults B = head . sort 
                             
-- ¿Gana un jugador dado un estado? 
win :: Player -> State -> Bool 
win p (State [a, b, c, d, e, f, g, h, i ]) = any (all (Player p==)) [ [a, b, c], [d, e, f], [g, h, i] 
                                                                    , [a, d, g], [b, e, h], [c, f, i] 
                                                                    , [a, e, i], [c, e, g] ] 
 
-- Expande una rama registrando estadísticas (en un juego más complejo puede limitarse la profundidad) 
deepStep :: Player -> State -> Int -> Step 
deepStep !p !st !i = 
  let State cs' = move p st i 
      mv  = [i + 1 | i <- [0..8], cs'!!i == N] 
      st' = State cs' 
  in  case (win p st', null mv) of 
        (True,     _ ) -> if p == B then Step Win i [] else Step Loss i [] 
        (False, True ) -> Step Draw i [] 
        (False, False) -> let p' = if p == A then B else A 
                              ns = map (deepStep p' st') mv 
                          in  Step (combineSubResults p' $ map result ns) i ns 
 
-- Genera el árbol de jugadas (la raíz no contiene movimiento) 
gameTree :: Player -> Step 
gameTree p = let ns = map (deepStep p emptyState) [1..9] 
             in  Step (combineSubResults p $ map result ns) 0 ns 
 
-- Un selector de nivel es aquel que selecciona un movimiento de entre los posibles 
type StrategySelector = [Step] -> Step 
 
selectorEasy, selectorIntermediate, selectorAdvanced :: StrategySelector 
selectorEasy            = last . sortBy (compare `on` result) 
selectorIntermediate rs = case filter ((Draw==).result) rs of 
                            (r:_) -> r 
                            []    -> head rs 
selectorAdvanced        = head . sortBy (compare `on` result) 
 
-- Selección del jugador inicial 
startWithPlayer :: IO Player 
startWithPlayer = do 
  putStrLn "Select starting player: A (human) or B (computer)" 
  p <- getLine 
  case reads p of 
    [(x, [])] -> return x 
    _         -> putStrLn "Invalid player name!" >> startWithPlayer 
 
-- Selección de dificultad 
skillLevel :: IO StrategySelector 
skillLevel = do 
  putStrLn "Select the skill level: [E]asy, [I]ntermediate or [A]dvanced" 
  s <- getLine 
  case s of 
    "E" -> return selectorEasy 
    "I" -> return selectorIntermediate 
    "A" -> return selectorAdvanced 
    _   -> putStrLn "Invalid skill level!" >> skillLevel 
 
-- Gestión de la partida 
game :: StrategySelector -> Player -> State -> Step -> IO () 
game f A st@(State cs) sp = do 
  print st 
  putStrLn "Select cell to write [1..9]:" 
  i <- getLine 
  case reads i of 
    [(n, [])] -> case filter ((n==).move2) (inner sp) of 
                   [sp'] -> let st' = move A st n 
                            in  check A st' $ game f B st' sp' 
                   _     -> putStrLn "Invalid position!" >> game f A st sp 
    _         -> putStrLn "Enter number into [1..9]!" >> game f A st sp 
game f B st@(State cs) sp = do 
  print $ map result $ inner sp 
  let sp' = f (inner sp) 
      m2  = move2 sp' 
      st' = move B st m2 
  print $ result sp' 
  putStrLn $ "Computer move into " ++ show m2 
  check B st' $ game f A st' sp' 
 
check :: Player -> State -> IO () -> IO () 
check p st@(State cs) k = do 
  if win p st 
    then print st >> putStrLn ("Player " ++ show p ++ " win!!!") 
    else 
      if any (N==) cs 
        then k 
        else print st >> putStrLn "Players draw!!!" 
 
-- Jugar! 
play :: IO () 
play = do 
  player <- startWithPlayer 
  skill  <- skillLevel 
  game skill player emptyState (gameTree player) 
 
-- Jugando hasta el infinito 
main = play >> main 
 
 
{- 
 
Select starting player: A (human) or B (computer) 
Select the skill level: [E]asy, [I]ntermediate or [A]dvanced 
[Draw,Draw,Draw,Draw,Draw,Draw,Draw,Draw,Draw] 
Draw 
Computer move into 1 
 B | · | · 
---+---+--- 
 · | · | · 
---+---+--- 
 · | · | · 
 
Select cell to write [1..9]: 
[Draw,Draw,Draw,Draw,Draw,Draw,Draw] 
Draw 
Computer move into 2 
 B | B | · 
---+---+--- 
 · | A | · 
---+---+--- 
 · | · | · 
 
Select cell to write [1..9]: 
[Loss,Loss,Draw,Loss,Loss] 
Draw 
Computer move into 7 
 B | B | A 
---+---+--- 
 · | A | · 
---+---+--- 
 B | · | · 
 
Select cell to write [1..9]: 
[Draw,Loss,Loss] 
Draw 
Computer move into 6 
 B | B | A 
---+---+--- 
 A | A | B 
---+---+--- 
 B | · | · 
 
Select cell to write [1..9]: 
[Draw] 
Draw 
Computer move into 8 
 B | B | A 
---+---+--- 
 A | A | B 
---+---+--- 
 B | B | A 
 
Players draw!!! 
 
-} 
2 comentarios
0votos

Escrito por The Champ hace 4 años

.... en java .....
0votos

Escrito por josejuan hace 4 años

¿Y eso por qué?, ¿que relación tiene el lenguaje Java con el desafío propuesto? (ej. sí existen desafíos en que la dificultad está relacionada con el lenguaje usado, en este caso no)

Comenta la solución

Tienes que identificarte para poder publicar tu comentario.