Initial gamestate modeling.
2 files changed, 266 insertions(+), 0 deletions(-)

A README.md
A => src/panacea_lib/gamestate.ml
A README.md +19 -0
@@ 0,0 1,19 @@ 
+# Panacea
+
+**A Pandemic bot.**
+
+- I'm homebound/self-quarantine during Coronavirus
+- I'm need to let off steam from my job's code
+- I'd like to do more OCaml
+
+I wrote a [Scrabble bot][] that taught me a lot, was super fun to write, and
+frankly probably got me a few jobs. I'm terrible at Pandemic and though it'd be
+fun to write a bot for it.
+
+This is GOFAI: none of that newfangled _Deep Learning_. Or not yet. Maybe after
+I get it to play enough games I can use it to generate data or something.
+
+## TODO
+
+- Model a decent gamestate, flow, render, REPL? Very bare minimum.
+- Start filling it in with proper cards, characters, actions.

          
A => src/panacea_lib/gamestate.ml +247 -0
@@ 0,0 1,247 @@ 
+open Base
+(**
+ * The Gamestate contains everything you need to properly model a game in-progress.
+ *)
+
+type difficulty = 
+  | Introductory  (* 4 Epidemic cards *)
+  | Standard      (* 5 Epidemic cards *)
+  | Heroic        (* 6 Epidemic cards *)
+
+type role = 
+  (* - As an action, take any discarded Event card and store it on this card.
+   * - When you play the stored Event card, remove it from the game.
+   *)
+  | ContingencyPlannner
+
+  (* - Move another player's paw as if it were yours.
+   * - As an action, move any pawn to a city with another pawn.
+   *)
+  | Dispatcher
+
+  (* - Remove all disease cubes of one color when doing Treat Disease.
+   * - Automatically remove cubes of cured diseases from the city you are in (and prevent
+   *   them from being placed there).
+   *)
+  | Medic
+
+  (* - As an action, build a research center in the city you are in (no City card needed).
+   * - Once per turn as an action, move from a research station to any city by discarding
+   *   by discarding any City card.
+   *)
+  | OperationsExpert
+
+  (* - Prevent disease cube placements (and outbreaks) in the city you are in and all cities
+   *   connected to it.
+   *)
+  | QuarantineSpecialist
+
+  (* - As an action, you may give (or a player can take) any City card from your hand.
+   *   You must both be in the same city. The card dooes not have to match the city
+   *   you are in.
+   *)
+  | Researcher
+
+  (* - You only need 4 cards of the same color to do the Discover a Cure action.
+   *)
+  | Scientist
+
+type infection_color =
+  | Black
+  | Blue
+  | Red
+  | Yellow
+
+type infection_city = 
+  | Algiers | Atlanta | Baghdad | Bangkok | Beijing | Bogota | BuenosAires | Cairo
+  | Chennai | Chicago | Delhi | Essen | HoChiMinhCity | HongKong | Istanbul | Jakarta
+  | Johannesburg | Karachi | Khartoum | Kinshasa | Kolkata | Lagos | Lima | London
+  | LosAngeles | Madrid | Manila | MexicoCity | Miami | Milan | Montreal | Moscow
+  | Mumbai | NewYork | Osaka | Paris | Riyadh | SanFrancisco | Santiago | SaoPaolo
+  | Seoul | Shanghai | StPetersburg | Sydney | Taipei | Tehran | Tokyo | Washington
+  [@@deriving sexp, compare]
+
+type player_card_event = {
+  name: string;
+  description: string;
+}
+
+type player_card = 
+  | Event of player_card_event
+  | Epidemic
+  | City of infection_city
+
+type player = {
+  role : role;
+  hand : player_card list;
+  location : infection_city;
+}
+
+type board_city = {
+  name : string;
+  natural_color : infection_color;
+  num_red : int;
+  num_blue : int;
+  num_black : int;
+  num_yellow : int;
+  has_research_station : bool;
+  neighbors : infection_city list;
+}
+
+module CityCompare = struct
+  module T = struct
+    type t = infection_city [@@deriving sexp_of, compare]
+  end
+  include T
+  include Comparable.Make(T)
+end
+
+type board = (infection_city, board_city, CityCompare.comparator_witness) Map.t
+
+type infection_card = infection_city
+
+type gamestate = {
+  players : player list;
+  board : board;
+  num_outbreaks : int;
+  curr_turn : int;
+  actions_remaining : int;
+  research_stations_remaining : int;
+  infection_deck : infection_card list;
+  infection_discard : infection_card list;
+  player_deck : player_card list;
+  player_discard : player_card list;
+  available_red_cubes : int;
+  available_blue_cubes : int;
+  available_black_cubes : int;
+  available_yellow_cubes : int;
+  is_red_cured : bool;
+  is_blue_cured : bool;
+  is_black_cured : bool;
+  is_yellow_cured : bool;
+}
+
+
+let empty_board = 
+  let initial = Map.empty (module CityCompare) in
+  let make_city_from (key, name, color, neighbors) = 
+    (key, {
+      name=name;
+      natural_color=color;
+      num_red=0;
+      num_blue=0;
+      num_black=0;
+      num_yellow=0;
+      has_research_station=false;
+      neighbors=neighbors
+    })
+  in
+  let cities = [
+    make_city_from(SanFrancisco, "San Francisco", Blue, [LosAngeles; Chicago; Tokyo; Manila]);
+    make_city_from(Chicago, "Chicago", Blue, [SanFrancisco; LosAngeles; MexicoCity; Atlanta; Montreal]);
+    make_city_from(Atlanta, "Atlanta", Red, [Miami; Chicago; Washington]);
+    make_city_from(Montreal, "Montreal", Blue, [Chicago; Washington; NewYork]);
+    make_city_from(Washington, "Washington", Blue, [Atlanta; Montreal; NewYork; Miami]);
+    make_city_from(NewYork, "NewYork", Blue, [Montreal; Washington; London; Madrid]);
+    make_city_from(London, "London", Blue, [NewYork; Madrid; Paris; Essen]);
+    make_city_from(Madrid, "Madrid", Blue, [NewYork; SaoPaolo; Algiers; Paris; London]);
+    make_city_from(Paris, "Paris", Blue, [London; Madrid; Algiers; Milan; Essen]);
+    make_city_from(Essen, "Essen", Blue, [London; Paris; Milan; StPetersburg]);
+    make_city_from(Milan, "Milan", Blue, [Essen; Paris; Istanbul]);
+    make_city_from(StPetersburg, "St. Petersburg", Blue, [Essen; Istanbul; Moscow]);
+
+    make_city_from(LosAngeles, "Los Angeles", Yellow, [SanFrancisco; Chicago; MexicoCity; Sydney]);
+    make_city_from(MexicoCity, "Mexico City", Yellow, [LosAngeles; Chicago; Miami; Bogota; Lima]);
+    make_city_from(Miami, "Miami", Yellow, [MexicoCity; Atlanta; Washington; Bogota]);
+    make_city_from(Bogota, "Bogota", Yellow, [MexicoCity; Miami; SaoPaolo; BuenosAires; Lima]);
+    make_city_from(Lima, "Lima", Yellow, [MexicoCity; Bogota; Santiago]);
+    make_city_from(Santiago, "Santiago", Yellow, [Lima]);
+    make_city_from(BuenosAires, "Buenos Aires", Yellow, [Bogota; SaoPaolo]);
+    make_city_from(SaoPaolo, "Sáo Paolo", Yellow, [BuenosAires; Bogota; Madrid; Lagos]);
+    make_city_from(Lagos, "Lagos", Yellow, [SaoPaolo; Kinshasa; Khartoum]);
+    make_city_from(Kinshasa, "Kinshasa", Yellow, [Lagos; Khartoum; Johannesburg]);
+    make_city_from(Khartoum, "Khartoum", Yellow, [Lagos; Kinshasa; Cairo]);
+    make_city_from(Johannesburg, "Johannesburg", Yellow, [Kinshasa; Khartoum]);
+
+    make_city_from(Algiers, "Algiers", Black, [Madrid; Paris; Istanbul; Cairo]);
+    make_city_from(Cairo, "Cairo", Black, [Algiers; Khartoum; Riyadh; Baghdad; Istanbul]);
+    make_city_from(Istanbul, "Istanbul", Black, [Milan; StPetersburg; Moscow; Baghdad; Cairo; Algiers]);
+    make_city_from(Moscow, "Moscow", Black, [StPetersburg; Tehran; Istanbul]);
+    make_city_from(Baghdad, "Baghdad", Black, [Cairo; Istanbul; Tehran; Karachi; Riyadh]);
+    make_city_from(Riyadh, "Riyadh", Black, [Cairo; Baghdad; Karachi]);
+    make_city_from(Tehran, "Tehran", Black, [Moscow; Baghdad; Karachi; Delhi]);
+    make_city_from(Karachi, "Karachi", Black, [Riyadh; Baghdad; Tehran; Delhi; Mumbai]);
+    make_city_from(Mumbai, "Mumbai", Black, [Karachi; Delhi; Chennai]);
+    make_city_from(Delhi, "Delhi", Black, [Tehran; Karachi; Mumbai; Kolkata; Chennai]);
+    make_city_from(Chennai, "Chennai", Black, [Mumbai; Delhi; Kolkata; Bangkok; Jakarta]);
+    make_city_from(Kolkata, "Kolkata", Black, [Delhi; Chennai; Bangkok; HongKong]);
+
+    make_city_from(Bangkok, "Bangkok", Red, [Chennai; Kolkata; Jakarta; HongKong; HoChiMinhCity]);
+    make_city_from(Jakarta, "Jakarta", Red, [Chennai; Bangkok; HoChiMinhCity; Sydney]);
+    make_city_from(Beijing, "Beijing", Red, [Shanghai; Seoul]);
+    make_city_from(Shanghai, "Shanghai", Red, [Beijing; Seoul; Tokyo; Taipei; HongKong]);
+    make_city_from(HongKong, "HongKong", Red, [Kolkata; Bangkok; HoChiMinhCity; Manila; Taipei; Shanghai]);
+    make_city_from(HoChiMinhCity, "Ho Chi Minh City", Red, [Bangkok; HongKong; Manila; Jakarta]);
+    make_city_from(Seoul, "Seoul", Red, [Beijing; Shanghai; Tokyo]);
+    make_city_from(Taipei, "Taipei", Red, [HongKong; Shanghai; Osaka; Manila]);
+    make_city_from(Manila, "Manila", Red, [HongKong; Taipei; SanFrancisco; Sydney; HoChiMinhCity]);
+    make_city_from(Sydney, "Sydney", Red, [Jakarta; Manila; LosAngeles]);
+    make_city_from(Tokyo, "Tokyo", Red, [Seoul; Shanghai; SanFrancisco]);
+    make_city_from(Osaka, "Osaka", Red, [Taipei; Tokyo]);
+  ] in
+  let add_city accum (name, x) =
+    Map.set accum ~key:name ~data:x
+  in
+  List.fold_left ~init:initial ~f:add_city cities 
+
+
+let starting_infection_deck =
+  [Algiers; Atlanta; Baghdad; Bangkok; Beijing; Bogota; BuenosAires; Cairo; Chennai; Chicago;
+    Delhi; Essen; HoChiMinhCity; HongKong; Istanbul; Jakarta; Johannesburg; Karachi; Khartoum;
+    Kinshasa; Kolkata; Lagos; Lima; London; LosAngeles; Madrid; Manila; MexicoCity; Miami; Milan;
+    Montreal; Moscow; Mumbai; NewYork; Osaka; Paris; Riyadh; SanFrancisco; Santiago; SaoPaolo;
+    Seoul; Shanghai; StPetersburg; Sydney; Taipei; Tehran; Tokyo; Washington] 
+
+
+let starting_player_deck = 
+  [
+    Event {name="Government Grant"; description="Add 1 research station to any city (no city card needed)."};
+    Event {name="Resilient Population"; description="Remove any 1 card in the infection discard pile from the game."};
+    Event {name="Forecast"; description="Draw, look at, and rearrange the top 6 cards of the infection deck. Put them back at on top."};
+    Event {name="One Quiet Night"; description="Skip the next Infect Cities step."};
+    Event {name="Airlift"; description="Move any 1 pawn to any city."};
+    City Algiers; City Atlanta; City Baghdad; City Bangkok; City Beijing; City Bogota; City BuenosAires; City Cairo;
+    City Chennai; City Chicago; City Delhi; City Essen; City HoChiMinhCity; City HongKong; City Istanbul; City Jakarta;
+    City Johannesburg; City Karachi; City Khartoum; City Kinshasa; City Kolkata; City Lagos; City Lima; City London; City LosAngeles;
+    City Madrid; City Manila; City MexicoCity; City Miami; City Milan; City Montreal; City Moscow; City Mumbai; City NewYork; City Osaka;
+    City Paris; City Riyadh; City SanFrancisco; City Santiago; City SaoPaolo; City Seoul; City Shanghai; City StPetersburg; City Sydney;
+    City Taipei; City Tehran; City Tokyo; City Washington
+  ]
+
+let empty_gamestate () = 
+  let players = [
+    {role=Dispatcher; hand=[]; location=Atlanta};
+    {role=ContingencyPlannner; hand=[]; location=Atlanta};
+    {role=Medic; hand=[]; location=Atlanta};
+  ] in
+  let board = empty_board in
+  {
+    players=players;
+    board=board;
+    curr_turn=0;
+    actions_remaining=4;
+    research_stations_remaining=6;
+    infection_deck=starting_infection_deck;
+    infection_discard=[];
+    player_deck=starting_player_deck;
+    player_discard=[];
+    available_red_cubes=24;
+    available_blue_cubes=24;
+    available_black_cubes=24;
+    available_yellow_cubes=24;
+    num_outbreaks=0;
+    is_red_cured=false;
+    is_blue_cured=false;
+    is_black_cured=false;
+    is_yellow_cured=false;
+  }