A tool for testing starfish
1import gleam/http
2import gleam/http/request
3import gleam/httpc
4import gleam/int
5import gleam/io
6import gleam/list
7import gleam/string
8import simplifile as file
9import starfish.{type Move}
10
11type ToMove {
12 UpdatedToMove
13 OriginalToMove
14}
15
16const fen_file = "silversuite.fen"
17
18pub fn main() -> Nil {
19 io.println("Loading games from file...")
20
21 let assert Ok(games) =
22 fen_file
23 |> file.read
24 as "Failed to read game file"
25
26 let games =
27 games
28 |> string.split("\n")
29 // Use `#` for comments in the file
30 |> list.filter(fn(game) { game != "" && !string.starts_with(game, "#") })
31
32 io.println("Loaded games.")
33
34 let outcomes = list.index_map(games, run_game) |> list.flatten
35
36 let #(wins, draws, losses) =
37 list.fold(outcomes, #(0, 0, 0), fn(tuple, outcome) {
38 let #(wins, draws, losses) = tuple
39 case outcome {
40 Updated -> #(wins + 1, draws, losses)
41 Draw(_) -> #(wins, draws + 1, losses)
42 Original -> #(wins, draws, losses + 1)
43 }
44 })
45
46 io.println(
47 "Updated version won "
48 <> int.to_string(wins)
49 <> " times.\nThe bots drew "
50 <> int.to_string(draws)
51 <> " times.\nOriginal version won "
52 <> int.to_string(losses)
53 <> " times.",
54 )
55
56 Nil
57}
58
59type Outcome {
60 Updated
61 Original
62 Draw(starfish.DrawReason)
63}
64
65fn run_game(fen: String, i: Int) -> List(Outcome) {
66 let assert Ok(game) = starfish.try_from_fen(fen)
67 as "Failed to parse fen string"
68
69 io.println("Running match " <> int.to_string(i + 1) <> ", position " <> fen)
70
71 update_fen(fen)
72
73 let first_outcome = run_game_loop(game, UpdatedToMove, UpdatedPlaysWhite)
74
75 io.println("Finished first game of match, " <> print_outcome(first_outcome))
76
77 io.println("Running again with reversed colours...")
78
79 update_fen(fen)
80
81 let second_outcome = run_game_loop(game, OriginalToMove, UpdatedPlaysBlack)
82
83 io.println("Finished second game of match, " <> print_outcome(second_outcome))
84
85 [first_outcome, second_outcome]
86}
87
88fn print_outcome(outcome: Outcome) -> String {
89 case outcome {
90 Original -> "original version won."
91 Updated -> "updated version won."
92 Draw(starfish.FiftyMoves) -> "fifty quiet moves occurred"
93 Draw(starfish.InsufficientMaterial) -> "there was insufficient material"
94 Draw(starfish.Stalemate) -> "it was stalemate"
95 Draw(starfish.ThreefoldRepetition) -> "the position was repeated 3 times"
96 }
97}
98
99type Configuration {
100 UpdatedPlaysWhite
101 UpdatedPlaysBlack
102}
103
104fn run_game_loop(
105 game: starfish.Game,
106 to_move: ToMove,
107 configuration: Configuration,
108) -> Outcome {
109 case starfish.state(game), configuration {
110 starfish.Draw(reason), _ -> Draw(reason)
111 starfish.BlackWin, UpdatedPlaysBlack | starfish.WhiteWin, UpdatedPlaysWhite ->
112 Updated
113 starfish.BlackWin, UpdatedPlaysWhite | starfish.WhiteWin, UpdatedPlaysBlack ->
114 Original
115 starfish.Continue, _ -> {
116 let best_move = get_best_move(game, to_move)
117
118 let to_move = case to_move {
119 OriginalToMove -> UpdatedToMove
120 UpdatedToMove -> OriginalToMove
121 }
122
123 run_game_loop(
124 starfish.apply_move(game, best_move),
125 to_move,
126 configuration,
127 )
128 }
129 }
130}
131
132fn update_fen(fen: String) -> Nil {
133 let assert Ok(original_req) = request.to(original_url <> "/fen")
134
135 let assert Ok(response) =
136 httpc.send(
137 original_req |> request.set_body(fen) |> request.set_method(http.Post),
138 )
139 assert response.status == 200
140
141 let assert Ok(updated_req) = request.to(updated_url <> "/fen")
142
143 let assert Ok(response) =
144 httpc.send(
145 updated_req |> request.set_body(fen) |> request.set_method(http.Post),
146 )
147 assert response.status == 200
148
149 Nil
150}
151
152const updated_url = "http://0.0.0.0:8000"
153
154const original_url = "http://0.0.0.0:8001"
155
156fn get_best_move(game: starfish.Game, to_move: ToMove) -> Move {
157 let #(url, opposing_url) = case to_move {
158 UpdatedToMove -> #(updated_url, original_url)
159 OriginalToMove -> #(original_url, updated_url)
160 }
161
162 let assert Ok(request) = request.to(url <> "/get_move")
163 let assert Ok(response) = httpc.send(request)
164 assert response.status == 200
165
166 let assert Ok(move) = starfish.parse_move(response.body, game)
167
168 let assert Ok(request) = request.to(opposing_url <> "/move")
169 let assert Ok(response) =
170 httpc.send(
171 request
172 |> request.set_body(response.body)
173 |> request.set_method(http.Post),
174 )
175 assert response.status == 200
176
177 move
178}