1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-19 04:41:36 +03:00
juvix/examples/milestone/TicTacToe/Logic/Game.juvix
janmasrovira 5c754846aa
Add initial support for examples in Html documentation (#1442)
* support examples in judoc

* fix line jumps

* make --open depend on the os
2022-08-09 19:09:47 +02:00

193 lines
5.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--- Tic-tac-toe is a paper-and-pencil game for two players who take turns marking the spaces
--- in a three-by-three grid with X or O.
---
--- The player who succeeds in placing three of their marks in a horizontal, vertical, or
--- diagonal row is the winner. It is a solved game, with a forced draw assuming best play from both players.
---
module Logic.Game;
open import Stdlib.Data.Nat.Ord;
open import Stdlib.Prelude;
-- Render Utils
infixr 5 ++str;
--- Concatenation of ;String;s
axiom ++str : String → String → String;
compile ++str {
c ↦ "concat";
};
--- Concatenates a list of strings
---
--- ;concat (("a" ∷ nil) ∷ ("b" ∷ nil)); evaluates to ;"a" ∷ "b" ∷ nil;
concat : List String → String;
concat ≔ foldl (++str) "";
surround : String → List String → List String;
surround x xs ≔ (x ∷ intersperse x xs) ++ (x ∷ nil);
intercalate : String → List String → String;
intercalate sep xs ≔ concat (intersperse sep xs);
--- Joins a list of strings with the newline character
unlines : List String → String;
unlines ≔ intercalate "\n";
-- Symbol
--- A symbol represents a token that can be placed in a square
inductive Symbol {
--- The circle token
O : Symbol;
--- The cross token
X : Symbol;
};
--- Equality for ;Symbol;s
==Symbol : Symbol → Symbol → Bool;
==Symbol O O ≔ true;
==Symbol X X ≔ true;
==Symbol _ _ ≔ false;
--- Turns ;O; into ;X; and ;X; into ;O;
switch : Symbol → Symbol;
switch O ≔ X;
switch X ≔ O;
--- Textual representation of a ;Symbol;
showSymbol : Symbol → String;
showSymbol O ≔ "O";
showSymbol X ≔ "X";
-- Square
--- A square is each of the holes in a board
inductive Square {
--- An empty square has a ;; that uniquely identifies it
empty : → Square;
--- An occupied square has a ;Symbol; in it
occupied : Symbol → Square;
};
-- --- A row in a board implemented as ;List Square;
-- ---
-- --- >>> Row;
-- Row : Type;
-- Row ≔ List Square;
-- A column in a board implemented as ;List Square;
-- Column : Type;
-- Column ≔ Row;
--- Equality for ;Square;s
==Square : Square → Square → Bool;
==Square (empty m) (empty n) ≔ m == n;
==Square (occupied s) (occupied t) ≔ ==Symbol s t;
==Square _ _ ≔ false;
--- Textual representation of a ;Square;
showSquare : Square → String;
showSquare (empty n) ≔ " " ++str natToStr n ++str " ";
showSquare (occupied s) ≔ " " ++str showSymbol s ++str " ";
-- TODO use a type alias?
--- A grid of squares
inductive Board {
board : List (List Square) → Board;
};
--- Returns the list of numbers corresponding to the empty ;Square;s
possibleMoves : List Square → List ;
possibleMoves nil ≔ nil;
possibleMoves ((empty n) ∷ xs) ≔ n ∷ possibleMoves xs;
possibleMoves (_ ∷ xs) ≔ possibleMoves xs;
--- ;true; if all the ;Square;s in the list are equal
full : List Square → Bool;
full (a ∷ b ∷ c ∷ nil) ≔ (==Square a b) && (==Square b c);
diagonals : List (List Square) → List (List Square);
diagonals ((a1 ∷ _ ∷ b1 ∷ nil) ∷ (_ ∷ c ∷ _ ∷ nil) ∷ (b2 ∷ _ ∷ a2 ∷ nil) ∷ nil) ≔ (a1 ∷ c ∷ a2 ∷ nil) ∷ (b1 ∷ c ∷ b2 ∷ nil) ∷ nil;
columns : List (List Square) → List (List Square);
columns ≔ transpose;
rows : List (List Square) → List (List Square);
rows ≔ id;
--- Textual representation of a ;(List Square);
showRow : (List Square) → String;
showRow xs ≔ concat (surround "|" (map showSquare xs));
showBoard : Board → String;
showBoard (board squares) ≔ unlines (surround "+---+---+---+" (map showRow squares));
-- Error
inductive Error {
--- no error occurred
noError : Error;
--- a non-fatal error occurred
continue : String → Error;
--- a fatal occurred
terminate : String → Error;
};
-- GameState
inductive GameState {
state : Board → Symbol → Error → GameState;
};
--- Textual representation of a ;GameState;
showGameState : GameState → String;
showGameState (state b _ _) ≔ showBoard b;
--- Projects the player
player : GameState → Symbol;
player (state _ p _) ≔ p;
--- initial ;GameState;
beginState : GameState;
beginState ≔ state
(board (map (map empty) ((one ∷ two ∷ three ∷ nil) ∷ (four ∷ five ∷ six ∷ nil) ∷ (seven ∷ eight ∷ nine ∷ nil) ∷ nil)))
X
noError;
--- ;true; if some player has won the game
won : GameState → Bool;
won (state (board squares) _ _) ≔ any full (diagonals squares ++ rows squares ++ columns squares);
--- ;true; if there is a draw
draw : GameState → Bool;
draw (state (board squares) _ _) ≔ null (possibleMoves (flatten squares));
-- Move
replace : Symbol → → Square → Square;
replace player k (empty n) ≔ if (n Stdlib.Data.Nat.Ord.== k) (occupied player) (empty n);
replace _ _ s ≔ s;
checkState : GameState → GameState;
checkState (state b p e) ≔
if (won (state b p e))
(state b p (terminate ("Player " ++str (showSymbol (switch p)) ++str " wins!")))
(if (draw (state b p e))
(state b p (terminate "It's a draw!"))
(state b p e));
playMove : Maybe → GameState → GameState;
playMove nothing (state b p _) ≔
state b p (continue "\nInvalid number, try again\n");
playMove (just k) (state (board s) player e) ≔
if (not (elem (==) k (possibleMoves (flatten s))))
(state (board s) player (continue "\nThe square is already occupied, try again\n"))
(checkState (state (board (map (map (replace player k)) s))
(switch player)
noError));
validMove : → Maybe ;
validMove n ≔ if ((n <= nine) && (n >= one)) (just n) nothing;
end;