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
open Core

type t = Tile.t list [@@deriving yojson { exn = true }]

let empty = []
let is_empty = List.is_empty
let length = List.length
let ( @ ) rack tiles = rack @ tiles
let nth_exn = List.nth_exn
let exists = List.exists
let of_tile_list tiles = tiles

let rec remove_once eq item (from : t) =
  match from with
  | [] -> None
  | x :: xs when eq item x -> Some xs
  | x :: xs -> (
      match remove_once eq item xs with
      | None -> None
      | Some rest -> Some (x :: rest))

let rec remove_all from items =
  match items with
  | [] -> Some from
  | t :: rest -> (
      match remove_once Tile.equal t from with
      | None -> None
      | Some smaller -> remove_all smaller rest)

let get_total_points rack =
  rack |> List.fold ~init:0 ~f:(fun sum tile -> sum + Tile.get_points tile)

let has_required_tiles rack placed =
  let rec consume rack (tiles : Placed_tile.t list) =
    match tiles with
    | [] -> Some rack
    | { tile = needed; _ } :: rest ->
        let rec take acc = function
          | [] -> None
          | t :: ts when Tile.equal_with_expansion t needed ->
              consume (List.rev acc @ ts) rest
          | t :: ts -> take (t :: acc) ts
        in
        take [] rack
  in
  match consume rack placed with None -> false | Some _ -> true