From 28731a9c0279d6e9339ee30d9caa53bb162c7e2d Mon Sep 17 00:00:00 2001
From: Michael Leuschel <leuschel@uni-duesseldorf.de>
Date: Tue, 2 Jan 2024 17:40:19 +0100
Subject: [PATCH] add TicTacToe TLA+ version

---
 TicTacToe/tictactoe_grid.svg  |  18 +++
 TicTacToe/tictactoe_v2.tla    | 101 +++++++++++++++++
 TicTacToe/tictactoe_visb.json | 207 ++++++++++++++++++++++++++++++++++
 3 files changed, 326 insertions(+)
 create mode 100644 TicTacToe/tictactoe_grid.svg
 create mode 100644 TicTacToe/tictactoe_v2.tla
 create mode 100644 TicTacToe/tictactoe_visb.json

diff --git a/TicTacToe/tictactoe_grid.svg b/TicTacToe/tictactoe_grid.svg
new file mode 100644
index 0000000..50bf42f
--- /dev/null
+++ b/TicTacToe/tictactoe_grid.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns="http://www.w3.org/2000/svg"
+   height="400" width="400"
+   viewBox="-2 -2 55 40"
+   >
+       <text  text-align="left"  x="0"  y="35" font-size="2"
+        font-family="sans-serif">
+       <tspan id="visb_debug_messages_opt"> </tspan>
+
+    
+  <filter id="displacementFilter">
+    <feTurbulence type="turbulence" baseFrequency="0.05"
+        numOctaves="2" result="turbulence"/>
+    <feDisplacementMap in2="turbulence" in="SourceGraphic"
+        scale="1.2" xChannelSelector="R" yChannelSelector="G"/>
+  </filter>
+</svg>
diff --git a/TicTacToe/tictactoe_v2.tla b/TicTacToe/tictactoe_v2.tla
new file mode 100644
index 0000000..7b7760b
--- /dev/null
+++ b/TicTacToe/tictactoe_v2.tla
@@ -0,0 +1,101 @@
+---------------------------- MODULE tictactoe_v2 ----------------------------
+(* taken from https://elliotswart.github.io/pragmaticformalmodeling/ *)
+(* version adapted for TLA2B so that we can have B-style operations with parameters for VisB *)
+(* also contains GAME state definitions for ProB so that we can use MCTS *)
+EXTENDS Naturals
+
+VARIABLES 
+    board, \* board[1..3][1..3] A 3x3 tic-tac-toe board
+    nextTurn \* who goes next
+
+Pieces == {"X", "O", "_"} \* "_" represents a blank square
+
+Init ==
+        /\ nextTurn = "X" \* X always goes first
+        \* Every space in the board states blank
+        /\ board = [i \in 1..3 |-> [j \in 1..3 |-> "_"]]
+
+MoveO ==
+    \E i \in 1..3: \E j \in 1..3: \* There exists a position on the board
+        /\ nextTurn = "O" \* Only enabled on player's turn
+        /\ nextTurn' = "X" \* The future state of next turn is other player
+        /\ board[i][j] = "_" \* Where the board is currently empty
+        (********************************************************************)
+        (* The future state of board is the same, except a piece is in that *)
+        (* spot                                                             *)
+        (********************************************************************)
+        /\ board' = [board EXCEPT
+                        ![i][j] = "O"] 
+
+MoveX ==
+    \E i \in 1..3: \E j \in 1..3: \* There exists a position on the board
+        /\ nextTurn = "X" \* Only enabled on player's turn
+        /\ nextTurn' = "O" \* The future state of next turn is other player
+        /\ board[i][j] = "_" \* Where the board is currently empty
+        (********************************************************************)
+        (* The future state of board is the same, except a piece is in that *)
+        (* spot                                                             *)
+        (********************************************************************)
+        /\ board' = [board EXCEPT
+                        ![i][j] = "X"] 
+
+\* Every state, X will move if X's turn, O will move on O's turn
+Next == MoveX \/ MoveO
+
+\* A description of every possible game of tic-tac-toe
+\* will play until the board fills up, even if someone won
+Spec == Init /\ [][Next]_<<board,nextTurn>>
+
+(***************************************************************************)
+(* Invariants: The things we are checking for.                             *)
+(***************************************************************************)
+
+WinningPositions == {
+    \* Horizonal wins
+    {<<1,1>>, <<1,2>>, <<1,3>>},
+    {<<2,1>>, <<2,2>>, <<2,3>>},
+    {<<3,1>>, <<3,2>>, <<3,3>>},
+    \* Vertical wins
+    {<<1,1>>, <<2,1>>, <<3,1>>},
+    {<<1,2>>, <<2,2>>, <<3,2>>},
+    {<<1,3>>, <<2,3>>, <<3,3>>},
+    \* Diagonal wins
+    {<<1,1>>, <<2,2>>, <<3,3>>},
+    {<<3,1>>, <<2,2>>, <<1,3>>}
+}
+
+Won(player) == 
+    \* A player has won if there exists a winning position
+    \E winningPosition \in WinningPositions:
+        \* Where all the needed spaces
+        \A neededSpace \in winningPosition:
+            \* are occupied by one player
+            board[neededSpace[1]][neededSpace[2]] = player
+
+XHasNotWon == ~Won("X")
+OHasNotWon == ~Won("O")
+
+BoardFilled ==
+    \* There does not exist
+    ~\E i \in 1..3, j \in 1..3:
+        \* an empty space
+        LET space == board[i][j] IN
+        space = "_"
+
+\* It's not a stalemate if one player has won or the board is not filled
+NotStalemate ==
+    \/ Won("X")
+    \/ Won("O")
+    \/ ~BoardFilled
+
+\* additions for ProB:
+VISB_JSON_FILE == "tictactoe_visb.json"
+GOAL == Won("O")
+\* the following Invariant is violated by this model
+INVARIANT == ~Won("O") \/ ~Won("X")
+\* additions for ProB so that we can apply MCTS auto play:
+GAME_MCTS_RUNS == 100
+GAME_PLAYER == IF nextTurn = "X" THEN "max" ELSE "min"
+GAME_OVER == IF Won("X") \/ Won("O") THEN TRUE ELSE FALSE
+GAME_VALUE == IF Won("X") THEN 1 ELSE 0
+=============================================================================
diff --git a/TicTacToe/tictactoe_visb.json b/TicTacToe/tictactoe_visb.json
new file mode 100644
index 0000000..c0128dd
--- /dev/null
+++ b/TicTacToe/tictactoe_visb.json
@@ -0,0 +1,207 @@
+{
+  "svg": "tictactoe_grid.svg",
+  "comment": "version which uses new svg object creation",
+  "definitions": [
+    { "name":"x_offset",
+      "value" : "4"
+    },
+    { "name":"center",
+      "value" : "%i.(i:INTEGER|5+10*(i-1))"
+    },
+    { "name":"vert_line_a",
+      "value":"%(i,j).(i:INTEGER & j:INTEGER | TO_STRING(center(i)+x_offset) ^ \" \" ^ TO_STRING(center(j)-x_offset)  ^ \" \" ^ TO_STRING(center(i)-x_offset) ^ \" \" ^ TO_STRING(center(j)+x_offset))"
+    },
+    { "name":"vert_line_b",
+      "value":"%(i,j).(i:INTEGER & j:INTEGER | TO_STRING(center(i)+x_offset) ^ \" \" ^ TO_STRING(center(j)+x_offset)  ^ \" \" ^ TO_STRING(center(i)-x_offset) ^ \" \" ^ TO_STRING(center(j)-x_offset))"
+    }
+  ],
+  "svg_objects":[
+    {
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":3}, 
+      "svg_class":"rect",
+      "id":"grid%0_%1",
+      "x":"10*(%0-1)",
+      "y":"10*(%1-1)",
+      "width":"10",
+      "height" : "10",
+      "stroke":"black",
+      "stroke-width":"0.25",
+      "fill":"white"
+    },
+    {
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":"3"}, 
+      "svg_class":"circle",
+      "id":"pieceO%0_%1",
+      "cx":"center(%0)",
+      "cy":"center(%1)",
+      "r":"4.5",
+      "stroke":"red",
+      "stroke-width":"0.5",
+      "style": "",
+      "fill":"white"
+    },
+    {
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":"3"}, 
+      "repeat": ["a","b"],
+      "svg_class":"polyline",
+      "id":"pieceX%0_%1_%2",
+      "points":"0 0 10 10",
+      "stroke":"blue",
+      "stroke-width":"0.5",
+      "style": "",
+      "fill":"white"
+    },
+    {
+      "svg_class":"rect",
+      "id":"winnerRect",
+      "x":"10*(5-1)",
+      "y":"10*(2-1)",
+      "width":"10",
+      "height" : "10",
+      "stroke":"black",
+      "stroke-width":"0.25",
+      "fill":"white"
+    },
+    {
+      "svg_class":"rect",
+      "id":"mctsRect",
+      "x":"10*(5-1)",
+      "y":"10.0*(2.0)+2.5",
+      "width":"10",
+      "height" : "5",
+      "stroke":"black",
+      "stroke-width":"0.25",
+      "fill":"lightgray"
+    },
+    {
+      "svg_class":"text",
+      "id":"winnerTxt",
+      "x":"40",
+      "y":"8",
+      "font-size":"3.0",
+      "font-family":"sans-serif",
+      "text":"Winner: "
+    },
+    {
+      "svg_class":"circle",
+      "id":"winnerO",
+      "cx":"center(5)",
+      "cy":"center(2)",
+      "r":"4.5",
+      "stroke":"red",
+      "stroke-width":"0.5",
+      "style": "",
+      "fill":"white"
+    },
+    {
+      "repeat": ["a","b"],
+      "svg_class":"polyline",
+      "id":"winnerX_%0",
+      "points":"0 0 10 10",
+      "stroke":"blue",
+      "stroke-width":"0.5",
+      "style": "",
+      "fill":"white"
+    },
+    {
+      "svg_class":"text",
+      "id":"mctsRectTxt",
+      "x":"41.6",
+      "y":"25.9",
+      "font-size":"2.5",
+      "font-family":"sans-serif",
+      "text":"MCTS"
+    }
+    ],
+  "items": [
+    { 
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":3}, 
+      "id":"pieceO%0_%1",
+      "attr": "visibility",
+      "value": "IF board(%0)(%1) = \"O\" THEN \"visible\" ELSE \"hidden\" END"
+    },
+    { 
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":3}, 
+      "repeat": ["a","b"],
+      "id":"pieceX%0_%1_%2",
+      "attr": "visibility",
+      "value": "IF board(%0)(%1) = \"X\" THEN \"visible\" ELSE \"hidden\" END"
+    },
+    { 
+      "id":"winnerO",
+      "attr": "visibility",
+      "value": "IF Won(\"O\") THEN \"visible\" ELSE \"hidden\" END"
+    },
+    { 
+      "repeat": ["a","b"],
+      "id":"winnerX_%0",
+      "attr": "visibility",
+      "value": "IF Won(\"X\") THEN \"visible\" ELSE \"hidden\" END"
+    },
+    { 
+      "id":"winnerTxt",
+      "attr": "text",
+      "value": "IF ran(union(ran(board)))={\"O\",\"X\"} & not(Won(\"X\")) & not(Won(\"O\")) THEN \"Draw !\" ELSE \"Winner:\" END"
+    },
+    { 
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":3}, 
+      "repeat": ["a"],
+      "id":"pieceX%0_%1_%2",
+      "attr": "points",
+      "value": "vert_line_a(%0,%1)"
+    },
+    { 
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":3}, 
+      "repeat": ["b"],
+      "id":"pieceX%0_%1_%2",
+      "attr": "points",
+      "value": "vert_line_b(%0,%1)"
+    },
+    { 
+      "id":"winnerX_a",
+      "attr": "points",
+      "value": "vert_line_a(5,2)"
+    },
+    { 
+      "id":"winnerX_b",
+      "attr": "points",
+      "value": "vert_line_b(5,2)"
+    }
+    ],
+  "events":[ 
+    {
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":3}, 
+      "id":"grid%0_%1",
+      "optional": "true",
+      "event":"MoveX",
+      "predicates": [ "i=%0", "j=%1"],
+      "hovers": [{ "attr":"fill", "enter":"gray", "leave":"white"}]
+    },
+    {
+      "for":{"from":1,"to":"3"}, 
+      "for":{"from":1,"to":3}, 
+      "id":"grid%0_%1",
+      "optional": "true",
+      "event":"MoveO",
+      "predicates": [ "i=%0", "j=%1"]
+    },
+    { 
+      "id":"mctsRect",
+      "event": "MCTS_AUTO_PLAY",
+      "hovers": [{ "attr":"fill", "enter":"green", "leave":"lightgray"}]
+    },
+    { 
+      "id":"mctsRectTxt",
+      "event": "MCTS_AUTO_PLAY",
+      "hovers": [{ "id":"mctsRect", "attr":"fill", "enter":"green", "leave":"lightgray"}]
+    }
+    ]
+}
-- 
GitLab