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
open Core
type t = Tile.t list [@@deriving to_yojson]
type distribution = { letter : char option; points : int; count : int }
let empty = []
let is_empty = List.is_empty
let length = List.length
let of_tile_list tiles = tiles
let to_tile_list bag = bag
let tile_distribution =
[
{ letter = Some 'A'; points = 1; count = 9 };
{ letter = Some 'B'; points = 3; count = 2 };
{ letter = Some 'C'; points = 3; count = 2 };
{ letter = Some 'D'; points = 2; count = 4 };
{ letter = Some 'E'; points = 1; count = 12 };
{ letter = Some 'F'; points = 4; count = 2 };
{ letter = Some 'G'; points = 2; count = 3 };
{ letter = Some 'H'; points = 4; count = 2 };
{ letter = Some 'I'; points = 1; count = 9 };
{ letter = Some 'J'; points = 8; count = 1 };
{ letter = Some 'K'; points = 5; count = 1 };
{ letter = Some 'L'; points = 1; count = 4 };
{ letter = Some 'M'; points = 3; count = 2 };
{ letter = Some 'N'; points = 1; count = 6 };
{ letter = Some 'O'; points = 1; count = 8 };
{ letter = Some 'P'; points = 3; count = 2 };
{ letter = Some 'Q'; points = 10; count = 1 };
{ letter = Some 'R'; points = 1; count = 6 };
{ letter = Some 'S'; points = 1; count = 4 };
{ letter = Some 'T'; points = 1; count = 6 };
{ letter = Some 'U'; points = 1; count = 4 };
{ letter = Some 'V'; points = 4; count = 2 };
{ letter = Some 'W'; points = 4; count = 2 };
{ letter = Some 'X'; points = 8; count = 1 };
{ letter = Some 'Y'; points = 4; count = 2 };
{ letter = Some 'Z'; points = 10; count = 1 };
{ letter = None; points = 0; count = 2 };
]
let shuffle ?(rng = Random.State.default) arr =
let a = Array.of_list arr in
for i = Array.length a - 1 downto 1 do
let j = Random.State.int rng (i + 1) in
Array.swap a i j
done;
Array.to_list a
let create_tile_bag ?rng () =
tile_distribution
|> List.concat_map ~f:(fun { letter; points; count } ->
List.init count ~f:(fun _ -> Tile.make letter points))
|> shuffle ?rng
let draw_tiles bag count =
let rec take n acc rest =
if n = 0 || List.is_empty rest then (List.rev acc, rest)
else take (n - 1) (List.hd_exn rest :: acc) (List.tl_exn rest)
in
take (Int.min count (List.length bag)) [] bag
let fill_rack rack bag =
let needed = 7 - Rack.length rack in
if needed <= 0 then (rack, bag)
else
let drawn, remaining = draw_tiles bag needed in
(Rack.(rack @ drawn), remaining)
let exchange_tiles (rack : Rack.t) bag tiles_to_exchange =
if List.length bag < 7 then None
else
match Rack.remove_all rack tiles_to_exchange with
| None -> None
| Some reduced_rack ->
let drawn, remaining_bag =
draw_tiles bag (List.length tiles_to_exchange)
in
let final_bag = shuffle (remaining_bag @ tiles_to_exchange) in
Some (Rack.(reduced_rack @ drawn), final_bag)
let tile_count bag = List.length bag