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
open Core
type t = Place of Placed_tile.t list | Pass | Exchange of Tile.t list
let apply_place_tiles game_state placed_tiles =
let valid, error, _ = Validation.validate_move game_state placed_tiles in
if not valid then (false, error, None, None, None)
else
let current_player =
List.nth_exn game_state.players game_state.current_player
in
let board_with_tiles =
List.fold placed_tiles ~init:game_state.board ~f:(fun b { tile; pos } ->
Board.place_tile_on_board b pos tile)
in
let extracted =
Word_extraction.extract_words board_with_tiles placed_tiles
in
let score = Scoring.calculate_move_score extracted placed_tiles in
let used_tiles = List.map placed_tiles ~f:(fun pt -> pt.tile) in
let rack_without =
Player.remove_tiles_from_rack (Player.get_rack current_player) used_tiles
in
let filled_rack, remaining_bag =
Tile_bag.fill_rack rack_without game_state.tile_bag
in
let updated_player =
Player.add_points (Player.set_rack current_player filled_rack) score
in
let new_players =
Game_state.update_players game_state.players game_state.current_player
updated_player
in
let next_state =
{
game_state with
board = board_with_tiles;
players = new_players;
tile_bag = remaining_bag;
current_player =
(game_state.current_player + 1) mod List.length game_state.players;
turn = game_state.turn + 1;
consecutive_passes = 0;
}
in
let end_state =
Game_state.check_game_end next_state updated_player
(List.length placed_tiles)
in
let next_state =
match end_state with
| None -> next_state
| Some e -> { next_state with end_state = Some e }
in
let word_strings = List.map extracted ~f:(fun w -> w.word) in
(true, None, Some next_state, Some score, Some word_strings)
let apply_pass game_state =
let consecutive = Game_state.(game_state.consecutive_passes + 1) in
let next_state =
{
game_state with
current_player =
(game_state.current_player + 1) mod List.length game_state.players;
turn = game_state.turn + 1;
consecutive_passes = consecutive;
}
in
let maybe_end : End_state.t option =
if consecutive >= List.length game_state.players * 2 then
let winner = Game_state.find_winner game_state.players in
Some
{
winner;
margin = Game_state.calculate_margin game_state.players winner;
reason = Bag_exhausted_stall consecutive;
}
else None
in
let next_state =
match maybe_end with
| None -> next_state
| Some e -> { next_state with end_state = Some e }
in
(true, None, Some next_state)
let apply_exchange game_state tiles =
let open Game_state in
let current = List.nth_exn game_state.players game_state.current_player in
match
Tile_bag.exchange_tiles (Player.get_rack current) game_state.tile_bag tiles
with
| None -> (false, Some "Not enough tiles in bag", None)
| Some (new_rack, new_bag) ->
let updated = Player.set_rack current new_rack in
let players =
update_players game_state.players game_state.current_player updated
in
let next_state =
{
game_state with
players;
tile_bag = new_bag;
current_player =
(game_state.current_player + 1) mod List.length game_state.players;
turn = game_state.turn + 1;
consecutive_passes = game_state.consecutive_passes + 1;
}
in
(true, None, Some next_state)
let apply game_state move =
match move with
| Place tiles ->
let success, error, new_state, score, words =
apply_place_tiles game_state tiles
in
(success, error, new_state, score, words)
| Pass ->
let success, error, new_state = apply_pass game_state in
(success, error, new_state, None, None)
| Exchange tiles ->
let success, error, new_state = apply_exchange game_state tiles in
(success, error, new_state, None, None)