~srpablo/squardle_bot

ref: 4d6ff97975f86c805c5fe0d88746e4abf7a4e369 squardle_bot/src/squardle_solver_lib/datatypes.ml -rw-r--r-- 3.1 KiB
4d6ff979 — Pablo Meier README and license a month ago
                                                                                
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
open Core

type tag_color =
  | Red of int
  | Yellow of int
  | Orange of int * int
  | White
  | Green
  | Black
[@@deriving show, eq]

(*
 * A "tag" is one of those little bits of information on a tile:
 * e.g. "this is green containing 'T'" or "this is red, on 'R',
 * with two arrows." It's just those two bits of data: the color,
 * any numbers on that color (the arrows on them), and the character
 * we've guessed.
 *
 * Then Pablo, my brother in Christ, what the hell is all this? Why
 * isn't this `type tag = {guess: string; color: tag_color}`?
 *
 * Well OCaml is very tYpE-sAfE, so you can't just say "stick it
 * in a set," you have to do the OCaml equivalent of defining
 * equals() and hashCode(). That's what all this crap is: wrapping
 * it in a struct/module, which in turn contains another little
 * struct/module that we can pass to the "convenience" functor
 * to easily make this Set-insertable. Details here:
 *
 * https://dev.realworldocaml.org/maps-and-hashtables.html#sets
 *)
module Tag = struct
  module T = struct
    type t = {
      guess: string;
      color: tag_color;
    }

    let create s c = { guess = s; color = c }

    let show_tag { guess; color } = Format.sprintf "{%s,%s}" guess (show_tag_color color)

    let compare { guess = g1; color = c1 } { guess = g2; color = c2 } =
      if Stdlib.( = ) c1 c2 then
        String.compare g1 g2
      else
        -1


    let sexp_of_t { guess = g1; color = c1 } =
      Sexp.List [Sexp.Atom g1; Sexp.Atom (show_tag_color c1)]
  end

  include T
  include Comparator.Make (T)
end

open Tag

type tile =
  | Solved of string * (Tag.t, Tag.comparator_witness) Set.t
  | Unsolved of (Tag.t, Tag.comparator_witness) Set.t

(*
 * Okay, fucking NOW what!? `pp_tile` and `equal_tile`?
 *
 * Yeah man, do you ever want to print your value, like `IO.inspect` in Elixir?
 * Well too bad, we have to define the equivalent of `toString()` here. And while
 * `ppx_derive` can do that for most simple datatypes, it didn't work with
 * Jane Street's Set, so I have to implement these myself on this type.
 *)
let pp_tile formatter t =
  match t with
  | Solved (x, _) -> Format.pp_print_string formatter ("Solved " ^ x)
  | Unsolved tags ->
      let tags_as_strings = tags |> Set.to_list |> List.map ~f:show_tag |> String.concat ~sep:"," in
      Format.pp_print_string formatter ("Unsolved set(" ^ tags_as_strings ^ ")")


let equal_tile t1 t2 =
  match t1, t2 with
  | Unsolved _, Solved _ -> false
  | Solved _, Unsolved _ -> false
  | Solved (x1, _), Solved (x2, _) -> String.equal x1 x2
  | Unsolved x1, Unsolved x2 -> Set.equal x1 x2


type board = tile list [@@deriving show, eq]

type game = {
  board: board;
  cursor: int;
  num_guesses: int;
}
[@@deriving show, eq]

type guess_type =
  | Row
  | Col

(* A single, itty-bitty util. *)
let explode_string str = String.to_list str |> List.map ~f:Char.to_string

let row_offsets = function
  | 0 -> [0; 1; 2; 3; 4]
  | 1 -> [8; 9; 10; 11; 12]
  | 2 -> [16; 17; 18; 19; 20]
  | _ -> failwith "invalid row index"


let col_offsets = function
  | 0 -> [0; 5; 8; 13; 16]
  | 1 -> [2; 6; 10; 14; 18]
  | 2 -> [4; 7; 12; 15; 20]
  | _ -> failwith "invalid col index"