summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--create.html3
-rw-r--r--data.js1467
-rw-r--r--images/icon_auc.pngbin0 -> 9979 bytes
-rw-r--r--images/icon_cartels.pngbin0 -> 14139 bytes
-rw-r--r--images/icon_farc.pngbin0 -> 9402 bytes
-rw-r--r--images/icon_govt.pngbin0 -> 8605 bytes
-rw-r--r--play.css428
-rw-r--r--play.html171
-rw-r--r--play.js822
-rw-r--r--rules.js1183
-rw-r--r--tokens.1x/shipment.pngbin0 -> 5248 bytes
-rw-r--r--tokens.2x/shipment.pngbin0 -> 14844 bytes
-rw-r--r--tools/gendata.js86
-rw-r--r--tools/genpieces.mjs1
-rw-r--r--tools/scale_tokens.sh82
-rw-r--r--tools/slice_tokens.sh13
16 files changed, 4169 insertions, 87 deletions
diff --git a/create.html b/create.html
index e69de29..b6ec3f7 100644
--- a/create.html
+++ b/create.html
@@ -0,0 +1,3 @@
+<p>
+<label><input type="checkbox" name="seeded" value="true">Seeded Propaganda Option</label>
+</p>
diff --git a/data.js b/data.js
new file mode 100644
index 0000000..76bce08
--- /dev/null
+++ b/data.js
@@ -0,0 +1,1467 @@
+const data = {
+ "first_pop": 1,
+ "first_city": 0,
+ "last_city": 10,
+ "first_dept": 11,
+ "last_pop": 22,
+ "last_dept": 26,
+ "first_foreign": 27,
+ "last_foreign": 31,
+ "first_loc": 32,
+ "last_loc": 49,
+ "coastal_spaces": [
+ 11,
+ 12,
+ 13,
+ 17
+ ],
+ "cards": [
+ null,
+ {
+ "number": 1,
+ "name": "1st Division",
+ "order": [
+ "Government",
+ "FARC",
+ "AUC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 2,
+ "name": "Ospina & Mora",
+ "order": [
+ "Government",
+ "FARC",
+ "AUC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 3,
+ "name": "Tapias",
+ "order": [
+ "Government",
+ "FARC",
+ "AUC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 4,
+ "name": "Caño Limón - Coveñas",
+ "order": [
+ "Government",
+ "FARC",
+ "Cartels",
+ "AUC"
+ ]
+ },
+ {
+ "number": 5,
+ "name": "Occidental & Ecopetrol",
+ "order": [
+ "Government",
+ "FARC",
+ "Cartels",
+ "AUC"
+ ]
+ },
+ {
+ "number": 6,
+ "name": "Oil Spill",
+ "order": [
+ "Government",
+ "FARC",
+ "Cartels",
+ "AUC"
+ ]
+ },
+ {
+ "number": 7,
+ "name": "7th Special Forces",
+ "order": [
+ "Government",
+ "AUC",
+ "FARC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 8,
+ "name": "Fuerza Aérea Colombiana",
+ "order": [
+ "Government",
+ "AUC",
+ "FARC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 9,
+ "name": "High Mountain Battalions",
+ "order": [
+ "Government",
+ "AUC",
+ "FARC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 10,
+ "name": "Blackhawks",
+ "order": [
+ "Government",
+ "AUC",
+ "Cartels",
+ "FARC"
+ ]
+ },
+ {
+ "number": 11,
+ "name": "National Defense & Security Council",
+ "order": [
+ "Government",
+ "AUC",
+ "Cartels",
+ "FARC"
+ ]
+ },
+ {
+ "number": 12,
+ "name": "Plan Colombia",
+ "order": [
+ "Government",
+ "AUC",
+ "Cartels",
+ "FARC"
+ ]
+ },
+ {
+ "number": 13,
+ "name": "Plan Meteoro",
+ "order": [
+ "Government",
+ "Cartels",
+ "FARC",
+ "AUC"
+ ]
+ },
+ {
+ "number": 14,
+ "name": "Tres Esquinas",
+ "order": [
+ "Government",
+ "Cartels",
+ "FARC",
+ "AUC"
+ ]
+ },
+ {
+ "number": 15,
+ "name": "War Tax",
+ "order": [
+ "Government",
+ "Cartels",
+ "FARC",
+ "AUC"
+ ]
+ },
+ {
+ "number": 16,
+ "name": "Coffee Prices",
+ "order": [
+ "Government",
+ "Cartels",
+ "AUC",
+ "FARC"
+ ]
+ },
+ {
+ "number": 17,
+ "name": "Madrid Donors",
+ "order": [
+ "Government",
+ "Cartels",
+ "AUC",
+ "FARC"
+ ]
+ },
+ {
+ "number": 18,
+ "name": "NSPD-18",
+ "order": [
+ "Government",
+ "Cartels",
+ "AUC",
+ "FARC"
+ ]
+ },
+ {
+ "number": 19,
+ "name": "General Offensive",
+ "order": [
+ "FARC",
+ "Government",
+ "AUC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 20,
+ "name": "Mono Jojoy",
+ "order": [
+ "FARC",
+ "Government",
+ "AUC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 21,
+ "name": "Raúl Reyes",
+ "order": [
+ "FARC",
+ "Government",
+ "AUC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 22,
+ "name": "Alfonso Cano",
+ "order": [
+ "FARC",
+ "Government",
+ "Cartels",
+ "AUC"
+ ]
+ },
+ {
+ "number": 23,
+ "name": "DoD Contractors",
+ "order": [
+ "FARC",
+ "Government",
+ "Cartels",
+ "AUC"
+ ]
+ },
+ {
+ "number": 24,
+ "name": "Operación Jaque",
+ "order": [
+ "FARC",
+ "Government",
+ "Cartels",
+ "AUC"
+ ]
+ },
+ {
+ "number": 25,
+ "name": "Ejército de Liberación Nacional",
+ "order": [
+ "FARC",
+ "AUC",
+ "Government",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 26,
+ "name": "Gramaje",
+ "order": [
+ "FARC",
+ "AUC",
+ "Government",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 27,
+ "name": "Misil Antiaéreo",
+ "order": [
+ "FARC",
+ "AUC",
+ "Government",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 28,
+ "name": "Hugo Chávez",
+ "order": [
+ "FARC",
+ "AUC",
+ "Cartels",
+ "Government"
+ ]
+ },
+ {
+ "number": 29,
+ "name": "Kill Zone",
+ "order": [
+ "FARC",
+ "AUC",
+ "Cartels",
+ "Government"
+ ]
+ },
+ {
+ "number": 30,
+ "name": "Peace Commission",
+ "order": [
+ "FARC",
+ "AUC",
+ "Cartels",
+ "Government"
+ ]
+ },
+ {
+ "number": 31,
+ "name": "Betancourt",
+ "order": [
+ "FARC",
+ "Cartels",
+ "Government",
+ "AUC"
+ ]
+ },
+ {
+ "number": 32,
+ "name": "Secuestrados",
+ "order": [
+ "FARC",
+ "Cartels",
+ "Government",
+ "AUC"
+ ]
+ },
+ {
+ "number": 33,
+ "name": "Sucumbíos",
+ "order": [
+ "FARC",
+ "Cartels",
+ "Government",
+ "AUC"
+ ]
+ },
+ {
+ "number": 34,
+ "name": "Airdropped AKs",
+ "order": [
+ "FARC",
+ "Cartels",
+ "AUC",
+ "Government"
+ ]
+ },
+ {
+ "number": 35,
+ "name": "Crop Substitution",
+ "order": [
+ "FARC",
+ "Cartels",
+ "AUC",
+ "Government"
+ ]
+ },
+ {
+ "number": 36,
+ "name": "Zona de Convivencia",
+ "order": [
+ "FARC",
+ "Cartels",
+ "AUC",
+ "Government"
+ ]
+ },
+ {
+ "number": 37,
+ "name": "Former Military",
+ "order": [
+ "AUC",
+ "Government",
+ "FARC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 38,
+ "name": "National Coordination Center",
+ "order": [
+ "AUC",
+ "Government",
+ "FARC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 39,
+ "name": "Soldados Campesinos",
+ "order": [
+ "AUC",
+ "Government",
+ "FARC",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 40,
+ "name": "Demobilization",
+ "order": [
+ "AUC",
+ "Government",
+ "Cartels",
+ "FARC"
+ ]
+ },
+ {
+ "number": 41,
+ "name": "Mancuso",
+ "order": [
+ "AUC",
+ "Government",
+ "Cartels",
+ "FARC"
+ ]
+ },
+ {
+ "number": 42,
+ "name": "Senado & Cámara",
+ "order": [
+ "AUC",
+ "Government",
+ "Cartels",
+ "FARC"
+ ]
+ },
+ {
+ "number": 43,
+ "name": "Calima Front",
+ "order": [
+ "AUC",
+ "FARC",
+ "Government",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 44,
+ "name": "Colombia Nueva",
+ "order": [
+ "AUC",
+ "FARC",
+ "Government",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 45,
+ "name": "Los Derechos Humanos",
+ "order": [
+ "AUC",
+ "FARC",
+ "Government",
+ "Cartels"
+ ]
+ },
+ {
+ "number": 46,
+ "name": "Limpieza",
+ "order": [
+ "AUC",
+ "FARC",
+ "Cartels",
+ "Government"
+ ]
+ },
+ {
+ "number": 47,
+ "name": "Pinto & del Rosario",
+ "order": [
+ "AUC",
+ "FARC",
+ "Cartels",
+ "Government"
+ ]
+ },
+ {
+ "number": 48,
+ "name": "Unión Sindical Obrera",
+ "order": [
+ "AUC",
+ "FARC",
+ "Cartels",
+ "Government"
+ ]
+ },
+ {
+ "number": 49,
+ "name": "Bloques",
+ "order": [
+ "AUC",
+ "Cartels",
+ "Government",
+ "FARC"
+ ]
+ },
+ {
+ "number": 50,
+ "name": "Carabineros",
+ "order": [
+ "AUC",
+ "Cartels",
+ "Government",
+ "FARC"
+ ]
+ },
+ {
+ "number": 51,
+ "name": "Pipeline Repairs",
+ "order": [
+ "AUC",
+ "Cartels",
+ "Government",
+ "FARC"
+ ]
+ },
+ {
+ "number": 52,
+ "name": "Castaño",
+ "order": [
+ "AUC",
+ "Cartels",
+ "FARC",
+ "Government"
+ ]
+ },
+ {
+ "number": 53,
+ "name": "Criminal Air Force",
+ "order": [
+ "AUC",
+ "Cartels",
+ "FARC",
+ "Government"
+ ]
+ },
+ {
+ "number": 54,
+ "name": "Deserters & Defectors",
+ "order": [
+ "AUC",
+ "Cartels",
+ "FARC",
+ "Government"
+ ]
+ },
+ {
+ "number": 55,
+ "name": "DEA Agents",
+ "order": [
+ "Cartels",
+ "Government",
+ "FARC",
+ "AUC"
+ ]
+ },
+ {
+ "number": 56,
+ "name": "Drogas La Rebaja",
+ "order": [
+ "Cartels",
+ "Government",
+ "FARC",
+ "AUC"
+ ]
+ },
+ {
+ "number": 57,
+ "name": "Op Millennium",
+ "order": [
+ "Cartels",
+ "Government",
+ "FARC",
+ "AUC"
+ ]
+ },
+ {
+ "number": 58,
+ "name": "General Serrano",
+ "order": [
+ "Cartels",
+ "Government",
+ "AUC",
+ "FARC"
+ ]
+ },
+ {
+ "number": 59,
+ "name": "Salcedo",
+ "order": [
+ "Cartels",
+ "Government",
+ "AUC",
+ "FARC"
+ ]
+ },
+ {
+ "number": 60,
+ "name": "The Chess Player",
+ "order": [
+ "Cartels",
+ "Government",
+ "AUC",
+ "FARC"
+ ]
+ },
+ {
+ "number": 61,
+ "name": "Air Bridge",
+ "order": [
+ "Cartels",
+ "FARC",
+ "Government",
+ "AUC"
+ ]
+ },
+ {
+ "number": 62,
+ "name": "Amazonía",
+ "order": [
+ "Cartels",
+ "FARC",
+ "Government",
+ "AUC"
+ ]
+ },
+ {
+ "number": 63,
+ "name": "Narco-War",
+ "order": [
+ "Cartels",
+ "FARC",
+ "Government",
+ "AUC"
+ ]
+ },
+ {
+ "number": 64,
+ "name": "Cocaine Labs",
+ "order": [
+ "Cartels",
+ "FARC",
+ "AUC",
+ "Government"
+ ]
+ },
+ {
+ "number": 65,
+ "name": "Poppies",
+ "order": [
+ "Cartels",
+ "FARC",
+ "AUC",
+ "Government"
+ ]
+ },
+ {
+ "number": 66,
+ "name": "Tingo María",
+ "order": [
+ "Cartels",
+ "FARC",
+ "AUC",
+ "Government"
+ ]
+ },
+ {
+ "number": 67,
+ "name": "Mexican Traffickers",
+ "order": [
+ "Cartels",
+ "AUC",
+ "Government",
+ "FARC"
+ ]
+ },
+ {
+ "number": 68,
+ "name": "Narco-Subs",
+ "order": [
+ "Cartels",
+ "AUC",
+ "Government",
+ "FARC"
+ ]
+ },
+ {
+ "number": 69,
+ "name": "Riverines & Fast Boats",
+ "order": [
+ "Cartels",
+ "AUC",
+ "Government",
+ "FARC"
+ ]
+ },
+ {
+ "number": 70,
+ "name": "Ayahuasca Tourism",
+ "order": [
+ "Cartels",
+ "AUC",
+ "FARC",
+ "Government"
+ ]
+ },
+ {
+ "number": 71,
+ "name": "Darién",
+ "order": [
+ "Cartels",
+ "AUC",
+ "FARC",
+ "Government"
+ ]
+ },
+ {
+ "number": 72,
+ "name": "Sicarios",
+ "order": [
+ "Cartels",
+ "AUC",
+ "FARC",
+ "Government"
+ ]
+ },
+ {
+ "number": 73,
+ "name": "Propaganda!",
+ "order": null
+ },
+ {
+ "number": 74,
+ "name": "Propaganda!",
+ "order": null
+ },
+ {
+ "number": 75,
+ "name": "Propaganda!",
+ "order": null
+ },
+ {
+ "number": 76,
+ "name": "Propaganda!",
+ "order": null
+ }
+ ],
+ "spaces": [
+ {
+ "type": "city",
+ "id": "Bogota",
+ "name": "Bogotá",
+ "pop": 8,
+ "adjacent": [
+ 35,
+ 42,
+ 43,
+ 45,
+ 19,
+ 22,
+ 14,
+ 20
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Cali",
+ "name": "Cali",
+ "pop": 3,
+ "adjacent": [
+ 12,
+ 13,
+ 20,
+ 33,
+ 37,
+ 41
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Medellin",
+ "name": "Medellín",
+ "pop": 3,
+ "adjacent": [
+ 12,
+ 18,
+ 39,
+ 40
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Bucaramanga",
+ "name": "Bucaramanga",
+ "pop": 2,
+ "adjacent": [
+ 18,
+ 19,
+ 42,
+ 47
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Ibague",
+ "name": "Ibagué",
+ "pop": 2,
+ "adjacent": [
+ 12,
+ 18,
+ 20,
+ 40,
+ 41,
+ 42
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Santa Marta",
+ "name": "Santa Marta",
+ "pop": 2,
+ "adjacent": [
+ 11,
+ 17,
+ 32,
+ 46
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Cartagena",
+ "name": "Cartagena",
+ "pop": 1,
+ "adjacent": [
+ 11,
+ 32,
+ 38
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Cucuta",
+ "name": "Cúcuta",
+ "pop": 1,
+ "adjacent": [
+ 19,
+ 31,
+ 44,
+ 49
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Neiva",
+ "name": "Neiva",
+ "pop": 1,
+ "adjacent": [
+ 14,
+ 16,
+ 20,
+ 34,
+ 43
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Pasto",
+ "name": "Pasto",
+ "pop": 1,
+ "adjacent": [
+ 13,
+ 16,
+ 20,
+ 28,
+ 33,
+ 34,
+ 36
+ ]
+ },
+ {
+ "type": "city",
+ "id": "Sincelejo",
+ "name": "Sincelejo",
+ "pop": 1,
+ "adjacent": [
+ 11,
+ 12,
+ 18,
+ 38,
+ 39,
+ 48
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Atlantico",
+ "name": "Atlántico",
+ "pop": 1,
+ "adjacent": [
+ 5,
+ 6,
+ 10,
+ 17,
+ 18,
+ 19,
+ 32,
+ 38,
+ 46,
+ 47,
+ 48,
+ 49
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Choco",
+ "name": "Chocó",
+ "pop": 1,
+ "adjacent": [
+ 1,
+ 2,
+ 4,
+ 10,
+ 13,
+ 18,
+ 20,
+ 29,
+ 37,
+ 39,
+ 40,
+ 41
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Narino",
+ "name": "Nariño",
+ "pop": 1,
+ "adjacent": [
+ 1,
+ 9,
+ 12,
+ 20,
+ 28,
+ 33,
+ 36,
+ 37
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Meta West",
+ "name": "Meta West",
+ "pop": 1,
+ "adjacent": [
+ 0,
+ 8,
+ 15,
+ 16,
+ 20,
+ 22,
+ 35,
+ 43
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Guaviare",
+ "name": "Guaviare",
+ "pop": 1,
+ "adjacent": [
+ 14,
+ 16,
+ 22,
+ 23,
+ 24,
+ 25,
+ 35
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Putumayo",
+ "name": "Putumayo",
+ "pop": 1,
+ "adjacent": [
+ 8,
+ 9,
+ 14,
+ 15,
+ 20,
+ 25,
+ 26,
+ 28,
+ 30,
+ 34
+ ]
+ },
+ {
+ "type": "mountain",
+ "id": "Cesar",
+ "name": "Cesar",
+ "pop": 1,
+ "adjacent": [
+ 5,
+ 11,
+ 18,
+ 19,
+ 31,
+ 46,
+ 47,
+ 48,
+ 49
+ ]
+ },
+ {
+ "type": "mountain",
+ "id": "Antioquia",
+ "name": "Antioquia",
+ "pop": 2,
+ "adjacent": [
+ 2,
+ 3,
+ 4,
+ 10,
+ 11,
+ 12,
+ 17,
+ 19,
+ 20,
+ 39,
+ 40,
+ 42,
+ 46,
+ 47,
+ 48,
+ 49
+ ]
+ },
+ {
+ "type": "mountain",
+ "id": "Santander",
+ "name": "Santander",
+ "pop": 2,
+ "adjacent": [
+ 0,
+ 3,
+ 7,
+ 11,
+ 17,
+ 18,
+ 20,
+ 21,
+ 22,
+ 31,
+ 45,
+ 46,
+ 47,
+ 48,
+ 49
+ ]
+ },
+ {
+ "type": "mountain",
+ "id": "Huila",
+ "name": "Huila",
+ "pop": 2,
+ "adjacent": [
+ 0,
+ 1,
+ 4,
+ 8,
+ 9,
+ 12,
+ 13,
+ 14,
+ 16,
+ 18,
+ 19,
+ 33,
+ 34,
+ 41,
+ 42,
+ 43
+ ]
+ },
+ {
+ "type": "grassland",
+ "id": "Arauca",
+ "name": "Arauca",
+ "pop": 1,
+ "adjacent": [
+ 19,
+ 22,
+ 23,
+ 31,
+ 44,
+ 45
+ ]
+ },
+ {
+ "type": "grassland",
+ "id": "Meta East",
+ "name": "Meta East",
+ "pop": 1,
+ "adjacent": [
+ 0,
+ 14,
+ 15,
+ 19,
+ 21,
+ 23,
+ 35,
+ 45
+ ]
+ },
+ {
+ "type": "grassland",
+ "id": "Vichada",
+ "name": "Vichada",
+ "pop": 0,
+ "adjacent": [
+ 15,
+ 21,
+ 22,
+ 24,
+ 31
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Guainia",
+ "name": "Guainía",
+ "pop": 0,
+ "adjacent": [
+ 15,
+ 23,
+ 25,
+ 27,
+ 31
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Vaupes",
+ "name": "Vaupés",
+ "pop": 0,
+ "adjacent": [
+ 15,
+ 16,
+ 24,
+ 26,
+ 27
+ ]
+ },
+ {
+ "type": "forest",
+ "id": "Amazonas",
+ "name": "Amazonas",
+ "pop": 0,
+ "adjacent": [
+ 16,
+ 25,
+ 27,
+ 30
+ ]
+ },
+ {
+ "type": "foreign",
+ "id": "Brasil",
+ "name": "Brasil",
+ "pop": 0,
+ "adjacent": [
+ 24,
+ 25,
+ 26
+ ]
+ },
+ {
+ "type": "foreign",
+ "id": "Ecuador",
+ "name": "Ecuador",
+ "pop": 0,
+ "adjacent": [
+ 9,
+ 13,
+ 16,
+ 36
+ ]
+ },
+ {
+ "type": "foreign",
+ "id": "Panama",
+ "name": "Panamá",
+ "pop": 0,
+ "adjacent": [
+ 12
+ ]
+ },
+ {
+ "type": "foreign",
+ "id": "Peru",
+ "name": "Perú",
+ "pop": 0,
+ "adjacent": [
+ 16,
+ 26
+ ]
+ },
+ {
+ "type": "foreign",
+ "id": "Venezuela",
+ "name": "Venezuela",
+ "pop": 0,
+ "adjacent": [
+ 7,
+ 17,
+ 19,
+ 21,
+ 23,
+ 24,
+ 44,
+ 49
+ ]
+ },
+ {
+ "type": "road",
+ "name": "Santa Marta / Cartagena",
+ "id": "Santa Marta / Cartagena",
+ "econ": 1,
+ "adjacent": [
+ 5,
+ 6,
+ 11
+ ]
+ },
+ {
+ "type": "road",
+ "name": "Cali / Pasto",
+ "id": "Cali / Pasto",
+ "econ": 1,
+ "adjacent": [
+ 1,
+ 9,
+ 13,
+ 20
+ ]
+ },
+ {
+ "type": "road",
+ "name": "Neiva / Pasto",
+ "id": "Neiva / Pasto",
+ "econ": 1,
+ "adjacent": [
+ 8,
+ 9,
+ 16,
+ 20
+ ]
+ },
+ {
+ "type": "road",
+ "name": "Bogotá / San José",
+ "id": "Bogota / San Jose",
+ "econ": 1,
+ "adjacent": [
+ 14,
+ 15,
+ 22
+ ]
+ },
+ {
+ "type": "road",
+ "name": "Pasto / Tumaco",
+ "id": "Pasto / Tumaco",
+ "econ": 1,
+ "adjacent": [
+ 9,
+ 13,
+ 28
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Cali / Buenaventura",
+ "id": "Cali / Buenaventura",
+ "econ": 1,
+ "adjacent": [
+ 1,
+ 12,
+ 13
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Cartagena / Sincelejo",
+ "id": "Cartagena / Sincelejo",
+ "econ": 1,
+ "adjacent": [
+ 6,
+ 10,
+ 11
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Sincelejo / Medellín",
+ "id": "Sincelejo / Medellin",
+ "econ": 2,
+ "adjacent": [
+ 2,
+ 10,
+ 12,
+ 18
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Medellín / Ibagué",
+ "id": "Medellin / Ibague",
+ "econ": 1,
+ "adjacent": [
+ 2,
+ 4,
+ 12,
+ 18
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Ibagué / Cali",
+ "id": "Ibague / Cali",
+ "econ": 1,
+ "adjacent": [
+ 1,
+ 4,
+ 12,
+ 20
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Bucaramanga / Ibagué / Bogotá",
+ "id": "Bucaramanga / Ibague / Bogota",
+ "econ": 2,
+ "adjacent": [
+ 3,
+ 4,
+ 18,
+ 20
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Bogotá / Neiva",
+ "id": "Bogota / Neiva",
+ "econ": 2,
+ "adjacent": [
+ 8,
+ 14,
+ 20
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Cúcuta / Arauca",
+ "id": "Cucuta / Arauca",
+ "econ": 3,
+ "adjacent": [
+ 7,
+ 21,
+ 21,
+ 31
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Bogotá / Yopal",
+ "id": "Bogota / Yopal",
+ "econ": 2,
+ "adjacent": [
+ 19,
+ 21,
+ 22
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Santa Marta / Ayacucho",
+ "id": "Santa Marta / Ayacucho",
+ "econ": 2,
+ "adjacent": [
+ 5,
+ 11,
+ 17,
+ 18,
+ 19,
+ 47,
+ 48,
+ 49
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Bucaramanga / Ayacucho",
+ "id": "Bucaramanga / Ayacucho",
+ "econ": 2,
+ "adjacent": [
+ 3,
+ 11,
+ 17,
+ 18,
+ 19,
+ 46,
+ 48,
+ 49
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Sincelejo / Ayacucho",
+ "id": "Sincelejo / Ayacucho",
+ "econ": 3,
+ "adjacent": [
+ 10,
+ 11,
+ 17,
+ 18,
+ 19,
+ 46,
+ 47,
+ 49
+ ]
+ },
+ {
+ "type": "pipeline",
+ "name": "Cúcuta / Ayacucho",
+ "id": "Cucuta / Ayacucho",
+ "econ": 3,
+ "adjacent": [
+ 7,
+ 11,
+ 17,
+ 18,
+ 19,
+ 31,
+ 46,
+ 47,
+ 48
+ ]
+ }
+ ]
+}
+if (typeof module !== 'undefined') module.exports = data
diff --git a/images/icon_auc.png b/images/icon_auc.png
new file mode 100644
index 0000000..c910d32
--- /dev/null
+++ b/images/icon_auc.png
Binary files differ
diff --git a/images/icon_cartels.png b/images/icon_cartels.png
new file mode 100644
index 0000000..8723fab
--- /dev/null
+++ b/images/icon_cartels.png
Binary files differ
diff --git a/images/icon_farc.png b/images/icon_farc.png
new file mode 100644
index 0000000..fa2d5aa
--- /dev/null
+++ b/images/icon_farc.png
Binary files differ
diff --git a/images/icon_govt.png b/images/icon_govt.png
new file mode 100644
index 0000000..4d6c582
--- /dev/null
+++ b/images/icon_govt.png
Binary files differ
diff --git a/play.css b/play.css
new file mode 100644
index 0000000..3b0e9d6
--- /dev/null
+++ b/play.css
@@ -0,0 +1,428 @@
+main { background-color: #777; }
+
+#role_Government, header.govt.your_turn { background-color: skyblue; }
+#role_AUC, header.auc.your_turn { background-color: khaki; }
+#role_Cartels, header.cartels.your_turn { background-color: darkseagreen; }
+#role_FARC, header.farc.your_turn { background-color: lightcoral; }
+
+#log { background-color: whitesmoke; }
+#log > div { padding-left: 20px; text-indent: -12px; }
+#log .h1 { background-color: silver; font-weight: bold; padding-top:4px; padding-bottom:4px; }
+#log .h2 { background-color: gainsboro; padding-top:2px; padding-bottom:2px; }
+#log .h2.govt { background-color: #c6d1eb }
+#log .h2.farc { background-color: #ebc9be }
+#log .h2.auc { background-color: #eaddc4 }
+#log .h2.cartels { background-color: #c2d7c7 }
+#log .h3 { text-decoration: underline; }
+#log .tip { cursor: pointer; }
+#log .tip:hover { text-decoration: underline; }
+
+.action { cursor: pointer }
+
+.role.active span { text-decoration: underline; }
+
+#card_tip {
+ position: fixed;
+ z-index: 100;
+ right: 240px;
+ top: 60px;
+}
+
+#mapwrap {
+ box-shadow: 0px 1px 10px #0008;
+ width: 1650px;
+ height: 2550px;
+ margin-bottom: 36px;
+}
+
+#map {
+ width: 1650px;
+ height: 2550px;
+ background-repeat: no-repeat;
+ background-size: cover;
+}
+
+#map { background-image: url("map75.jpg") }
+@media (min-resolution: 97dpi) {
+#map { background-image: url("map150.jpg") }
+}
+
+#svgmap { position: absolute; }
+#tokens { position: absolute; }
+#pieces { position: absolute; }
+#cities { position: absolute; }
+#boxes { position: absolute; }
+
+path { fill: transparent; }
+path.action { fill: white; fill-opacity: 0.3; stroke: white; stroke-width: 4; }
+path.action.foreign { fill: gold; stroke: lemonchiffon; }
+path.action.forest { fill: limegreen; stroke: limegreen; }
+path.action.grassland { fill: greenyellow; stroke: greenyellow; }
+path.action.mountain { fill: chocolate; stroke: chocolate; }
+
+.box{position:absolute;box-sizing:border-box;}
+.box.action{z-index:100; border:4px solid lemonchiffon;}
+.box.city{border-radius:50%}
+.box.city.action{background-color:#6495ed55;border:4px solid cornflowerblue;}
+.box.loc{border-radius:50%}
+.box.loc.action{background-color:#fff5;border:4px solid white;}
+
+.piece { position: absolute; }
+.piece { background-position: center; background-repeat: no-repeat; background-size: contain; }
+.piece {
+ transition-property: top, left;
+ transition-duration: 700ms;
+ transition-timing-function: ease;
+}
+.piece {
+ filter: drop-shadow(0 1px 2px #000a);
+}
+.piece.action {
+ filter:
+ drop-shadow(0 -2px 0 white)
+ drop-shadow(0 2px 0 white)
+ drop-shadow(-2px 0 0 white)
+ drop-shadow(2px 0 0 white)
+ ;
+}
+
+.token {
+ position: absolute;
+ transition-property: top, left;
+ transition-duration: 700ms;
+ transition-timing-function: ease;
+ background-repeat: no-repeat;
+ border-radius: 8px;
+ border: 2px solid #333;
+ background-size: 45px 45px;
+ width: 45px;
+ height: 45px;
+}
+
+.token.terror,
+.token.sabotage,
+.token.active_opposition,
+.token.passive_opposition,
+.token.passive_support,
+.token.active_support,
+.token.reminder
+{
+ border-radius: 0px;
+ background-size: 36px 36px;
+ width: 36px;
+ height: 36px;
+}
+
+.token.shipment
+{
+ border-radius: 50%;
+ background-size: 50px 50px;
+ background-position: center;
+ width: 50px;
+ height: 50px;
+}
+.token.shipment { background-color: #01904b; border-color: #01904b; box-shadow: 0 0 0 1px #002c00, 0px 1px 4px #0008; }
+
+#token_total_support { background-color: #006cb7; border-color: #348cda #004d95 #004d95 #348cda; box-shadow: 0 0 0 1px #000d55, 0px 1px 4px #0008; }
+#token_prop_card { background-color: #80643e; border-color: #a0835c #614721 #614721 #a0835c; box-shadow: 0 0 0 1px #291200, 0px 1px 4px #0008; }
+#token_oppose_plus_bases { background-color: #d74729; border-color: #ff6f50 #ad1800 #ad1800 #ff6f50; box-shadow: 0 0 0 1px #5a0000, 0px 1px 4px #0008; }
+#token_el_presidente { background-color: #004e81; border-color: #23669b #003768 #003768 #23669b; box-shadow: 0 0 0 1px #000939, 0px 1px 4px #0008; }
+#token_aid { background-color: #6a8796; border-color: #8eacbc #486472 #486472 #8eacbc; box-shadow: 0 0 0 1px #08232f, 0px 1px 4px #0008; }
+.token.passive_support { background-color: #4b87c0; border-color: #70ade8 #266399 #266399 #70ade8; box-shadow: 0 0 0 1px #001e50, 0px 1px 4px #0008; }
+.token.passive_opposition { background-color: #b75f61; border-color: #df8384 #903c40 #903c40 #df8384; box-shadow: 0 0 0 1px #460005, 0px 1px 4px #0008; }
+.token.active_support { background-color: #0054a6; border-color: #286fc4 #003989 #003989 #286fc4; box-shadow: 0 0 0 1px #000051, 0px 1px 4px #0008; }
+.token.active_opposition { background-color: #991a1e; border-color: #b73a37 #7b0001 #7b0001 #b73a37; box-shadow: 0 0 0 1px #400000, 0px 1px 4px #0008; }
+.token.govt_control { background-color: #0054a6; border-color: #286fc4 #003989 #003989 #286fc4; box-shadow: 0 0 0 1px #000051, 0px 1px 4px #0008; }
+.token.farc_control { background-color: #991a1e; border-color: #b73a37 #7b0001 #7b0001 #b73a37; box-shadow: 0 0 0 1px #400000, 0px 1px 4px #0008; }
+.token.farc_zone { background-color: #ffc509; border-color: #fff75f #c68e00 #c68e00 #fff75f; box-shadow: 0 0 0 1px #5d2800, 0px 1px 4px #0008; }
+.token.terror { background-color: #535052; border-color: #6b686a #3c393b #3c393b #6b686a; box-shadow: 0 0 0 1px #120f11, 0px 1px 4px #0008; }
+.token.sabotage { background-color: #535052; border-color: #6b686a #3c393b #3c393b #6b686a; box-shadow: 0 0 0 1px #120f11, 0px 1px 4px #0008; }
+.token.unshaded { background-color: #9dadb3; border-color: #ccdce2 #718086 #718086 #ccdce2; box-shadow: 0 0 0 1px #222f34, 0px 1px 4px #0008; }
+.token.shaded { background-color: #465c80; border-color: #60779d #2d4264 #2d4264 #60779d; box-shadow: 0 0 0 1px #021230, 0px 1px 4px #0008; }
+.token.reminder.sucumbios { background-color: #6d5735; border-color: #89724f #523d1b #523d1b #89724f; box-shadow: 0 0 0 1px #210f00, 0px 1px 4px #0008; }
+.token.reminder.senado_farc { background-color: #cf1f30; border-color: #f74d51 #a7000b #a7000b #f74d51; box-shadow: 0 0 0 1px #590000, 0px 1px 4px #0008; }
+.token.reminder.senado_cartels { background-color: #6d9f3b; border-color: #94c865 #487703 #487703 #94c865; box-shadow: 0 0 0 1px #012e00, 0px 1px 4px #0008; }
+.token.reminder.senado_auc { background-color: #ffcf00; border-color: #fffa57 #c69700 #c69700 #fffa57; box-shadow: 0 0 0 1px #5b2e00, 0px 1px 4px #0008; }
+.token.reminder.darien { background-color: #8dc73f; border-color: #bdfa75 #609600 #609600 #bdfa75; box-shadow: 0 0 0 1px #0a3c00, 0px 1px 4px #0008; }
+
+#token_aid { background-image: url(tokens.1x/aid.png) }
+#token_el_presidente { background-image: url(tokens.1x/el_presidente.png) }
+#token_oppose_plus_bases { background-image: url(tokens.1x/oppose_plus_bases.png) }
+#token_prop_card { background-image: url(tokens.1x/prop_card.png) }
+#token_total_support { background-image: url(tokens.1x/total_support.png) }
+.token.shipment { background-image: url(tokens.1x/shipment.png) }
+.token.terror { background-image: url(tokens.1x/terror.png) }
+.token.sabotage { background-image: url(tokens.1x/sabotage.png) }
+.token.active_opposition { background-image: url(tokens.1x/active_opposition.png) }
+.token.passive_opposition { background-image: url(tokens.1x/passive_opposition.png) }
+.token.passive_support { background-image: url(tokens.1x/passive_support.png) }
+.token.active_support { background-image: url(tokens.1x/active_support.png) }
+.token.farc_control { background-image: url(tokens.1x/farc_control.png) }
+.token.govt_control { background-image: url(tokens.1x/govt_control.png) }
+.token.farc_zone { background-image: url(tokens.1x/farc_zone.png) }
+.token.unshaded.first_div { background-image: url(tokens.1x/unshaded_1st_div.png) }
+.token.unshaded.seventh_sf { background-image: url(tokens.1x/unshaded_7th_sf.png) }
+.token.unshaded.black_hawks { background-image: url(tokens.1x/unshaded_black_hawks.png) }
+.token.unshaded.meteoro { background-image: url(tokens.1x/unshaded_meteoro.png) }
+.token.unshaded.mtn_bns { background-image: url(tokens.1x/unshaded_mtn_bns.png) }
+.token.unshaded.ndsc { background-image: url(tokens.1x/unshaded_ndsc.png) }
+.token.unshaded.ospina { background-image: url(tokens.1x/unshaded_ospina.png) }
+.token.unshaded.tapias { background-image: url(tokens.1x/unshaded_tapias.png) }
+.token.shaded.first_div { background-image: url(tokens.1x/shaded_1st_div.png) }
+.token.shaded.seventh_sf { background-image: url(tokens.1x/shaded_7th_sf.png) }
+.token.shaded.black_hawks { background-image: url(tokens.1x/shaded_black_hawks.png) }
+.token.shaded.meteoro { background-image: url(tokens.1x/shaded_meteoro.png) }
+.token.shaded.mtn_bns { background-image: url(tokens.1x/shaded_mtn_bns.png) }
+.token.shaded.ndsc { background-image: url(tokens.1x/shaded_ndsc.png) }
+.token.shaded.ospina { background-image: url(tokens.1x/shaded_ospina.png) }
+.token.shaded.tapias { background-image: url(tokens.1x/shaded_tapias.png) }
+.token.reminder.darien { background-image: url(tokens.1x/reminder_darien.png) }
+.token.reminder.senado_auc { background-image: url(tokens.1x/reminder_senado_auc.png) }
+.token.reminder.senado_cartels { background-image: url(tokens.1x/reminder_senado_cartels.png) }
+.token.reminder.senado_farc { background-image: url(tokens.1x/reminder_senado_farc.png) }
+.token.reminder.sucumbios { background-image: url(tokens.1x/reminder_sucumbios.png) }
+
+@media (min-resolution: 97dpi) {
+#token_aid { background-image: url(tokens.2x/aid.png) }
+#token_el_presidente { background-image: url(tokens.2x/el_presidente.png) }
+#token_oppose_plus_bases { background-image: url(tokens.2x/oppose_plus_bases.png) }
+#token_prop_card { background-image: url(tokens.2x/prop_card.png) }
+#token_total_support { background-image: url(tokens.2x/total_support.png) }
+.token.shipment { background-image: url(tokens.2x/shipment.png) }
+.token.terror { background-image: url(tokens.2x/terror.png) }
+.token.sabotage { background-image: url(tokens.2x/sabotage.png) }
+.token.active_opposition { background-image: url(tokens.2x/active_opposition.png) }
+.token.passive_opposition { background-image: url(tokens.2x/passive_opposition.png) }
+.token.passive_support { background-image: url(tokens.2x/passive_support.png) }
+.token.active_support { background-image: url(tokens.2x/active_support.png) }
+.token.farc_control { background-image: url(tokens.2x/farc_control.png) }
+.token.govt_control { background-image: url(tokens.2x/govt_control.png) }
+.token.farc_zone { background-image: url(tokens.2x/farc_zone.png) }
+.token.unshaded.first_div { background-image: url(tokens.2x/unshaded_1st_div.png) }
+.token.unshaded.seventh_sf { background-image: url(tokens.2x/unshaded_7th_sf.png) }
+.token.unshaded.black_hawks { background-image: url(tokens.2x/unshaded_black_hawks.png) }
+.token.unshaded.meteoro { background-image: url(tokens.2x/unshaded_meteoro.png) }
+.token.unshaded.mtn_bns { background-image: url(tokens.2x/unshaded_mtn_bns.png) }
+.token.unshaded.ndsc { background-image: url(tokens.2x/unshaded_ndsc.png) }
+.token.unshaded.ospina { background-image: url(tokens.2x/unshaded_ospina.png) }
+.token.unshaded.tapias { background-image: url(tokens.2x/unshaded_tapias.png) }
+.token.shaded.first_div { background-image: url(tokens.2x/shaded_1st_div.png) }
+.token.shaded.seventh_sf { background-image: url(tokens.2x/shaded_7th_sf.png) }
+.token.shaded.black_hawks { background-image: url(tokens.2x/shaded_black_hawks.png) }
+.token.shaded.meteoro { background-image: url(tokens.2x/shaded_meteoro.png) }
+.token.shaded.mtn_bns { background-image: url(tokens.2x/shaded_mtn_bns.png) }
+.token.shaded.ndsc { background-image: url(tokens.2x/shaded_ndsc.png) }
+.token.shaded.ospina { background-image: url(tokens.2x/shaded_ospina.png) }
+.token.shaded.tapias { background-image: url(tokens.2x/shaded_tapias.png) }
+.token.reminder.darien { background-image: url(tokens.2x/reminder_darien.png) }
+.token.reminder.senado_auc { background-image: url(tokens.2x/reminder_senado_auc.png) }
+.token.reminder.senado_cartels { background-image: url(tokens.2x/reminder_senado_cartels.png) }
+.token.reminder.senado_farc { background-image: url(tokens.2x/reminder_senado_farc.png) }
+.token.reminder.sucumbios { background-image: url(tokens.2x/reminder_sucumbios.png) }
+}
+
+.cylinder { width: 44px; height: 48px; }
+.base { width: 44px; height: 38px; }
+.guerrilla { width: 29px; height: 36px; }
+.police, .troops { width: 29px; height: 36px; }
+
+.govt.cylinder { background-image: url(images/govt_cylinder.svg) }
+.govt.police { background-image: url(images/govt_police.svg) }
+.govt.troops { background-image: url(images/govt_troops.svg) }
+.govt.base { background-image: url(images/govt_base.svg) }
+.auc.cylinder { background-image: url(images/auc_cylinder.svg) }
+.auc.guerrilla { background-image: url(images/auc_guerrilla.svg) }
+.auc.guerrilla.active { background-image: url(images/auc_guerrilla_active.svg) }
+.auc.base { background-image: url(images/auc_base.svg) }
+.cartels.cylinder { background-image: url(images/cartels_cylinder.svg) }
+.cartels.guerrilla { background-image: url(images/cartels_guerrilla.svg) }
+.cartels.guerrilla.active { background-image: url(images/cartels_guerrilla_active.svg) }
+.cartels.base { background-image: url(images/cartels_base.svg) }
+.farc.cylinder { background-image: url(images/farc_cylinder.svg) }
+.farc.guerrilla { background-image: url(images/farc_guerrilla.svg) }
+.farc.guerrilla.active { background-image: url(images/farc_guerrilla_active.svg) }
+.farc.base { background-image: url(images/farc_base.svg) }
+
+#card_panel {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 18px;
+ padding: 0 18px;
+ max-width: 1614px;
+ margin: 36px auto;
+}
+
+#deck_outer {
+ position: relative;
+}
+#deck_size {
+ position: absolute;
+ right: 24px;
+ bottom: 16px;
+ font-size: 24px;
+ font-weight: bold;
+ color: white;
+}
+
+.card {
+ width: 248px;
+ height: 348px;
+ border-radius: 16px;
+ box-shadow: 1px 1px 4px #0008;
+ background-color: darkgreen;
+ background-size: cover;
+ background-repeat: no-repeat;
+ border: 1px solid #454;
+}
+
+.card.card_back{background-image:url(cards.1x/card_back.jpg)}
+.card.card_1{background-image:url(cards.1x/card_01.jpg)}
+.card.card_2{background-image:url(cards.1x/card_02.jpg)}
+.card.card_3{background-image:url(cards.1x/card_03.jpg)}
+.card.card_4{background-image:url(cards.1x/card_04.jpg)}
+.card.card_5{background-image:url(cards.1x/card_05.jpg)}
+.card.card_6{background-image:url(cards.1x/card_06.jpg)}
+.card.card_7{background-image:url(cards.1x/card_07.jpg)}
+.card.card_8{background-image:url(cards.1x/card_08.jpg)}
+.card.card_9{background-image:url(cards.1x/card_09.jpg)}
+.card.card_10{background-image:url(cards.1x/card_10.jpg)}
+.card.card_11{background-image:url(cards.1x/card_11.jpg)}
+.card.card_12{background-image:url(cards.1x/card_12.jpg)}
+.card.card_13{background-image:url(cards.1x/card_13.jpg)}
+.card.card_14{background-image:url(cards.1x/card_14.jpg)}
+.card.card_15{background-image:url(cards.1x/card_15.jpg)}
+.card.card_16{background-image:url(cards.1x/card_16.jpg)}
+.card.card_17{background-image:url(cards.1x/card_17.jpg)}
+.card.card_18{background-image:url(cards.1x/card_18.jpg)}
+.card.card_19{background-image:url(cards.1x/card_19.jpg)}
+.card.card_20{background-image:url(cards.1x/card_20.jpg)}
+.card.card_21{background-image:url(cards.1x/card_21.jpg)}
+.card.card_22{background-image:url(cards.1x/card_22.jpg)}
+.card.card_23{background-image:url(cards.1x/card_23.jpg)}
+.card.card_24{background-image:url(cards.1x/card_24.jpg)}
+.card.card_25{background-image:url(cards.1x/card_25.jpg)}
+.card.card_26{background-image:url(cards.1x/card_26.jpg)}
+.card.card_27{background-image:url(cards.1x/card_27.jpg)}
+.card.card_28{background-image:url(cards.1x/card_28.jpg)}
+.card.card_29{background-image:url(cards.1x/card_29.jpg)}
+.card.card_30{background-image:url(cards.1x/card_30.jpg)}
+.card.card_31{background-image:url(cards.1x/card_31.jpg)}
+.card.card_32{background-image:url(cards.1x/card_32.jpg)}
+.card.card_33{background-image:url(cards.1x/card_33.jpg)}
+.card.card_34{background-image:url(cards.1x/card_34.jpg)}
+.card.card_35{background-image:url(cards.1x/card_35.jpg)}
+.card.card_36{background-image:url(cards.1x/card_36.jpg)}
+.card.card_37{background-image:url(cards.1x/card_37.jpg)}
+.card.card_38{background-image:url(cards.1x/card_38.jpg)}
+.card.card_39{background-image:url(cards.1x/card_39.jpg)}
+.card.card_40{background-image:url(cards.1x/card_40.jpg)}
+.card.card_41{background-image:url(cards.1x/card_41.jpg)}
+.card.card_42{background-image:url(cards.1x/card_42.jpg)}
+.card.card_43{background-image:url(cards.1x/card_43.jpg)}
+.card.card_44{background-image:url(cards.1x/card_44.jpg)}
+.card.card_45{background-image:url(cards.1x/card_45.jpg)}
+.card.card_46{background-image:url(cards.1x/card_46.jpg)}
+.card.card_47{background-image:url(cards.1x/card_47.jpg)}
+.card.card_48{background-image:url(cards.1x/card_48.jpg)}
+.card.card_49{background-image:url(cards.1x/card_49.jpg)}
+.card.card_50{background-image:url(cards.1x/card_50.jpg)}
+.card.card_51{background-image:url(cards.1x/card_11.jpg)}
+.card.card_52{background-image:url(cards.1x/card_52.jpg)}
+.card.card_53{background-image:url(cards.1x/card_53.jpg)}
+.card.card_54{background-image:url(cards.1x/card_54.jpg)}
+.card.card_55{background-image:url(cards.1x/card_55.jpg)}
+.card.card_56{background-image:url(cards.1x/card_56.jpg)}
+.card.card_57{background-image:url(cards.1x/card_57.jpg)}
+.card.card_58{background-image:url(cards.1x/card_58.jpg)}
+.card.card_59{background-image:url(cards.1x/card_59.jpg)}
+.card.card_60{background-image:url(cards.1x/card_60.jpg)}
+.card.card_61{background-image:url(cards.1x/card_61.jpg)}
+.card.card_62{background-image:url(cards.1x/card_62.jpg)}
+.card.card_63{background-image:url(cards.1x/card_63.jpg)}
+.card.card_64{background-image:url(cards.1x/card_64.jpg)}
+.card.card_65{background-image:url(cards.1x/card_65.jpg)}
+.card.card_66{background-image:url(cards.1x/card_66.jpg)}
+.card.card_67{background-image:url(cards.1x/card_67.jpg)}
+.card.card_68{background-image:url(cards.1x/card_68.jpg)}
+.card.card_69{background-image:url(cards.1x/card_69.jpg)}
+.card.card_70{background-image:url(cards.1x/card_70.jpg)}
+.card.card_71{background-image:url(cards.1x/card_71.jpg)}
+.card.card_72{background-image:url(cards.1x/card_72.jpg)}
+.card.card_73, .card.card_74, .card.card_75, .card.card_76 {background-image:url(cards.1x/card_7x.jpg)}
+
+@media (min-resolution: 97dpi) {
+.card.card_back{background-image:url(cards.2x/card_back.jpg)}
+.card.card_1{background-image:url(cards.2x/card_01.jpg)}
+.card.card_2{background-image:url(cards.2x/card_02.jpg)}
+.card.card_3{background-image:url(cards.2x/card_03.jpg)}
+.card.card_4{background-image:url(cards.2x/card_04.jpg)}
+.card.card_5{background-image:url(cards.2x/card_05.jpg)}
+.card.card_6{background-image:url(cards.2x/card_06.jpg)}
+.card.card_7{background-image:url(cards.2x/card_07.jpg)}
+.card.card_8{background-image:url(cards.2x/card_08.jpg)}
+.card.card_9{background-image:url(cards.2x/card_09.jpg)}
+.card.card_10{background-image:url(cards.2x/card_10.jpg)}
+.card.card_11{background-image:url(cards.2x/card_11.jpg)}
+.card.card_12{background-image:url(cards.2x/card_12.jpg)}
+.card.card_13{background-image:url(cards.2x/card_13.jpg)}
+.card.card_14{background-image:url(cards.2x/card_14.jpg)}
+.card.card_15{background-image:url(cards.2x/card_15.jpg)}
+.card.card_16{background-image:url(cards.2x/card_16.jpg)}
+.card.card_17{background-image:url(cards.2x/card_17.jpg)}
+.card.card_18{background-image:url(cards.2x/card_18.jpg)}
+.card.card_19{background-image:url(cards.2x/card_19.jpg)}
+.card.card_20{background-image:url(cards.2x/card_20.jpg)}
+.card.card_21{background-image:url(cards.2x/card_21.jpg)}
+.card.card_22{background-image:url(cards.2x/card_22.jpg)}
+.card.card_23{background-image:url(cards.2x/card_23.jpg)}
+.card.card_24{background-image:url(cards.2x/card_24.jpg)}
+.card.card_25{background-image:url(cards.2x/card_25.jpg)}
+.card.card_26{background-image:url(cards.2x/card_26.jpg)}
+.card.card_27{background-image:url(cards.2x/card_27.jpg)}
+.card.card_28{background-image:url(cards.2x/card_28.jpg)}
+.card.card_29{background-image:url(cards.2x/card_29.jpg)}
+.card.card_30{background-image:url(cards.2x/card_30.jpg)}
+.card.card_31{background-image:url(cards.2x/card_31.jpg)}
+.card.card_32{background-image:url(cards.2x/card_32.jpg)}
+.card.card_33{background-image:url(cards.2x/card_33.jpg)}
+.card.card_34{background-image:url(cards.2x/card_34.jpg)}
+.card.card_35{background-image:url(cards.2x/card_35.jpg)}
+.card.card_36{background-image:url(cards.2x/card_36.jpg)}
+.card.card_37{background-image:url(cards.2x/card_37.jpg)}
+.card.card_38{background-image:url(cards.2x/card_38.jpg)}
+.card.card_39{background-image:url(cards.2x/card_39.jpg)}
+.card.card_40{background-image:url(cards.2x/card_40.jpg)}
+.card.card_41{background-image:url(cards.2x/card_41.jpg)}
+.card.card_42{background-image:url(cards.2x/card_42.jpg)}
+.card.card_43{background-image:url(cards.2x/card_43.jpg)}
+.card.card_44{background-image:url(cards.2x/card_44.jpg)}
+.card.card_45{background-image:url(cards.2x/card_45.jpg)}
+.card.card_46{background-image:url(cards.2x/card_46.jpg)}
+.card.card_47{background-image:url(cards.2x/card_47.jpg)}
+.card.card_48{background-image:url(cards.2x/card_48.jpg)}
+.card.card_49{background-image:url(cards.2x/card_49.jpg)}
+.card.card_50{background-image:url(cards.2x/card_50.jpg)}
+.card.card_51{background-image:url(cards.2x/card_11.jpg)}
+.card.card_52{background-image:url(cards.2x/card_52.jpg)}
+.card.card_53{background-image:url(cards.2x/card_53.jpg)}
+.card.card_54{background-image:url(cards.2x/card_54.jpg)}
+.card.card_55{background-image:url(cards.2x/card_55.jpg)}
+.card.card_56{background-image:url(cards.2x/card_56.jpg)}
+.card.card_57{background-image:url(cards.2x/card_57.jpg)}
+.card.card_58{background-image:url(cards.2x/card_58.jpg)}
+.card.card_59{background-image:url(cards.2x/card_59.jpg)}
+.card.card_60{background-image:url(cards.2x/card_60.jpg)}
+.card.card_61{background-image:url(cards.2x/card_61.jpg)}
+.card.card_62{background-image:url(cards.2x/card_62.jpg)}
+.card.card_63{background-image:url(cards.2x/card_63.jpg)}
+.card.card_64{background-image:url(cards.2x/card_64.jpg)}
+.card.card_65{background-image:url(cards.2x/card_65.jpg)}
+.card.card_66{background-image:url(cards.2x/card_66.jpg)}
+.card.card_67{background-image:url(cards.2x/card_67.jpg)}
+.card.card_68{background-image:url(cards.2x/card_68.jpg)}
+.card.card_69{background-image:url(cards.2x/card_69.jpg)}
+.card.card_70{background-image:url(cards.2x/card_70.jpg)}
+.card.card_71{background-image:url(cards.2x/card_71.jpg)}
+.card.card_72{background-image:url(cards.2x/card_72.jpg)}
+.card.card_73, .card.card_74, .card.card_75, .card.card_76 {background-image:url(cards.2x/card_7x.jpg)}
+}
diff --git a/play.html b/play.html
new file mode 100644
index 0000000..9b68a9d
--- /dev/null
+++ b/play.html
@@ -0,0 +1,171 @@
+<!DOCTYPE html>
+<!-- vim:set nowrap: -->
+<html lang="en">
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta charset="UTF-8">
+<title>ANDEAN ABYSS</title>
+<link id="favicon" rel="icon" href="favicon.svg">
+<link rel="stylesheet" href="/fonts/fonts.css">
+<link rel="stylesheet" href="/common/play.css">
+<link rel="stylesheet" href="play.css">
+<script defer src="/common/play.js"></script>
+<script defer src="data.js"></script>
+<script defer src="play.js"></script>
+</head>
+<body>
+
+<div id="card_tip" class="hide"></div>
+
+<header>
+ <div id="toolbar">
+ <div class="menu">
+ <div class="menu_title"><img src="/images/cog.svg"></div>
+ <div class="menu_popup">
+ <a class="menu_item" href="info/rulebook.html" target="_blank")">Rulebook</a>
+ <a class="menu_item" href="info/playbook.html" target="_blank")">Playbook</a>
+ <a class="menu_item" href="info/pac.html" target="_blank")">Player Aids</a>
+ <a class="menu_item" href="info/cards.html" target="_blank")">Cards</a>
+ <div class="menu_separator"></div>
+ <div class="menu_item" onclick="send_restart('Standard')">&#x26a0; Restart Standard</div>
+ <div class="menu_item" onclick="send_restart('Open')">&#x26a0; <s>Restart Open</s></div>
+ <div class="menu_item" onclick="send_restart('Short')">&#x26a0; Restart Short</div>
+ <div class="menu_item" onclick="send_restart('Quick')">&#x26a0; Restart Quick</div>
+ </div>
+ </div>
+ <div class="icon_button" onclick="toggle_pieces()"><img src="/images/earth-america.svg"></div>
+ <div class="icon_button" onclick="toggle_zoom()"><img src="/images/magnifying-glass.svg"></div>
+ <div class="icon_button" onclick="toggle_log()"><img src="/images/scroll-quill.svg"></div>
+ </div>
+ <div id="prompt"></div>
+ <div id="actions"></div>
+</header>
+
+<aside>
+ <div id="roles">
+ <div class="role" id="role_Government">
+ <div class="role_name"><span>Government</span>
+ <div class="role_user">-</div>
+ </div>
+ </div>
+ <div class="role" id="role_AUC">
+ <div class="role_name"><span>AUC</span>
+ <div class="role_user">-</div>
+ </div>
+ </div>
+ <div class="role" id="role_Cartels">
+ <div class="role_name"><span>Cartels</span>
+ <div class="role_user">-</div>
+ </div>
+ </div>
+ <div class="role" id="role_FARC">
+ <div class="role_name"><span>FARC</span>
+ <div class="role_user">-</div>
+ </div>
+ </div>
+ </div>
+ <div id="log"></div>
+</aside>
+
+<main>
+
+<div id="mapwrap">
+<div id="map">
+<svg id="svgmap" width="1650" height="2550" viewBox="0 0 1650 2550">
+<path class="mountain" id="Cesar" d="M824 725c-7-2-6-15-18-10-5 2-17-2-15-8 2-9 11-14 10-24 3-10-7-17-11-26l-15-29c-3-7-11-14-10-22 4-10-2-23-14-24-7-2-11-11-15-18-5-8-10-17-17-24-2-9-6-17-13-22-1-8-6-21 5-23 8-4 16-12 22-19 6-6 9-14 12-22 2-11 2-22 1-33 7 4 15 7 24 6 16 0 33 1 49-2 6-5 15-1 20-7 6-7 13-11 20-16 6-8 16-9 23-13 7-5 10-15 19-16 8-7 19-8 29-12 9-3 19-2 25-10 8-4 16-7 20-15 4-9 10-17 11-28 1-4 0-14 2-5 5 8 18 9 24 2 8-3 10-13 9-19 10 2 16-8 25-11 8-7 17 4 25 5 6 2 15 4 18 9l13 22c-3 7-14 10-19 17-8 4-5 19-15 20-7 4-15 8-24 7l-27 7c-7 2-15 4-21 9-6 9-21 6-24 19-5 9-11 18-15 29-1 4-10 6-14 9-9-3-19 0-24 9-5 7-7 15-12 22-9 2-11 12-14 20l-11 22c-6 9-15 15-18 24-2 12 0 25-6 35-6 9-1 20-4 30-5 6 0 15 1 20-8 5-13 15-12 24-6 5-12 10-15 17-3 8-6 17-12 24-7 5-16 14-12 23 3 10 14 9 22 8 5 1 16-4 9 4 1 7-5 13-11 15z"/>
+<path class="forest" id="Atlantico" d="M704 714c-9 1-16-7-23-11l-20-13c-4-8-11-11-20-13-9-4-16-11-24-16-5-5-11-8-18-11-6-6-15-8-23-4-9 4-17 9-25 15-6 1-13-8-20-9 5-9-5-18-3-25 8-7 6-21-1-29 5-8 13-17 6-27 0-8 2-13 10-12 7 0-1-11 6-12 5-11 9-22 9-34-1-9 1-16 10-18 7-5 11-12 21-10 11 1 16-9 18-19 3-5 8-18 13-15 2 10 8 19 14 27 6 6 13 11 20 15s15 4 24 5c3 6 0 15 3 22 2 9 2 17 5 25 5 7 15 9 18 19 4 7 9 12 12 20 8 8 11 18 14 28 8 7 16 15 20 25l15 27c5 9 1 20 2 30-1 12-14 3-22 5-14 0-27 7-39 15z"/>
+<path class="forest" id="Choco" d="M406 1433c-10-3-20-1-30-1 2-6 13-10 11-19 2-10-7-19-16-20-8 0-16 3-23-3-6-5-17 5-21-1-1-9-13-12-21-11-8-2-14 13-18 2-10-2-10-15-13-21 1-8-1-15 7-19 2-9 9-16 8-25 1-9 6-17 4-27 5-8 0-17-5-24-2-9-2-19-3-29-2-11 1-21 1-32-3-7-1-17-8-23-3-4-10-11-1-11 11 1 21-6 25-16 3-8 1-18-3-25l-11-23c-3-6-16-15-13-20 12-3 11-19 13-28 0-12 7-26-4-34-2-9-11-16-20-14-1-8-7-16-14-21-1-6 2-22-9-28-8-4-22-13-12-22 7-5-2-19 10-18 6-2 4-14 10-7 10 5 22 1 27-8 5-6 13-7 18-13 9-7 2-19 5-25 9-5 21-9 21-21 0-9-10-12-9-21-3-9-7-18-14-25-3-4-4-19 2-9 8 3 12 9 16 17 3 6 12 9 15 13 4 10 6 19 6 29 0 12 14 15 24 14 9 0 14-9 14-17 2-10-3-19-3-29 0-9 1-19-1-28-4-7-7-16-15-19 10-5 24-5 32-14 3-9 15-8 19-16 6-5 12-9 19-11 10-5 12-15 18-24 6-4 7-19 12-19 0 10-2 19-1 28-1 9 4 18 8 24 6 6 7 15 16 19 8 2 16 8 26 8 10 1-2 13 1 20 0 8-4 16-3 25-1 10 6 20 15 24 10 3 17 15 28 10 7-4 20 10 10 13s-13 15-10 24c5 7-8 13-4 22 3 10-3 19-4 29-2 8-5 15-10 22-1 8 2 17-2 25-4 5-3 19-9 19-9-2-17 1-24 3-11 3-20 9-28 16-7 2-20-1-16 11-2 13 0 27-2 40-1 11 1 22 8 30 3 7 9 12 15 17 6 6 13 10 21 12 9 2 14 6 13 16 3 10-14 13-12 24-2 9 8 15 7 23-8 7 3 20-4 25-9 3-19 5-26 11-5 6-13 8-21 7-9 5 0 20-3 29-3 11-3 21 1 32 2 9 6 18 2 27-9 5 0 25-15 21-9-1-19 8-18 18s8 18-1 26c-4 8 2 16 0 24 0 8-2 17-8 22z"/>
+<path class="mountain" id="Antioquia" d="M545 1225c-7-7-22-8-28-13 8-6 1-15 2-23 7-6 15-14 11-25-2-8-12-13-4-21-2-14 13-12 21-18 7-5 15-10 20-18 7-8 11-18 13-29 2-16-1-32-7-47-6-8-12-16-20-22-11-4-5-12-2-20 1-9 6-16 9-24 2-7-2-15 2-23 2-9 3-19 8-27 9-5 4-18 8-24 9-6 6-18 7-26-1-11 2-22-4-32-7-7-16-11-25-14-7-8-17-6-25-9-4-6 4-16 7-23s1-16 6-23c4-6 13-7 15-15 14-12 16-31 15-48 0-7-6-15 4-16 8-6 15-2 21 4 7 7 19 9 24 17 3 11 15 9 23 13l30 19c8 4 12 12 21 13 10 2 17-5 24-9 10-3 23-17 32-8 3 11 1 22 4 33-1 8-7 18-1 27 7 6 4 17 3 25-3 9-2 18 4 25 5 7 6 19 5 27-2 9-1 18 4 25 1 12 11 19 22 20 8 1 17 7 6 12-5 9-19 4-23 10l-3 35c-8 3-15 8-23 10-9 5-4 18-14 23l-19 22c-4 9-18 6-20 18 0 11-9 18-18 20-10 2-22 2-28 11l-12 20c-3 9 2 19-3 28-3 8-2 19-8 25-9 6-5 18-6 27s-3 19-7 27c-6 7-16 10-25 11-10 2-22-2-31 6-4 2-10 4-15 4z"/>
+<path class="mountain" id="Santander" d="M832 1302c-12 2-15-11-22-18-9-7-17-15-28-18-7-3-15-4-23-5-13-1-27 2-39 7-7 0-13 15-16 3-7-6-16-8-22-16-5-5-6-14-13-17-9-2-18-9-22-17-2-8-5-15 0-23 2-7 2-16 9-21 5-9 1-20 5-30 2-10 4-20 8-30 8-5 2-18 10-21 11-5 24-7 32-17 5-8 19-7 21-17 0-11 12-17 17-25 5-7 12-12 16-20 5-2 11-5 14-8l18 20c12 13 30 17 47 16 8 0 16-4 24-6 8-5 17-12 23-20 6-6 7-16 12-23 1-14 3-30-4-43-4-7-7-15-14-20-5-5-12-10-19-12-7-7-20-3-27-8-8-11-19-21-32-23-11-3 1-20-10-25-4-6-3-17-3-24 6-8 4-19-3-26-4-9-5-22 0-31 1-10-9-15-7-26-1-4-2-19 3-9 2 13 14 5 22 8 13 2 27 1 38-5 9-5 14-15 23-19 10-6 12 11 16 17 8 7 9 19 15 28 6 8 3 20 11 27 5 3 9 10 1 11-6 9-23-1-19 13l-4 33c-1 19 8 39 24 51 3 3 11 5 8 12 0 9-1 18 6 25 7 5 10 13 20 16 7 3 15 8 21 13 6 6 13 13 8 22-5 8-8 16-10 26-4 8-13 15-11 25 0 9-14 3-15 13-4 10-17 9-20 19-2 9-5 21-1 30 8 3 9 17-1 17-8 5-9 21 1 23 4 2 7 7 13 7 7 4 6 17-2 18-7 5-10 15-11 22-8 4-12 12-18 16-6 7-3 18-10 24-4 9-9 17-18 21-10 7-3 20-10 28-5 7-15 0-21 5-4 2-6 6-11 7z"/>
+<path class="grassland" id="Arauca" d="M1078 1267c-9 0-14-7-17-14-7-6-17-11-27-11-7-3-11-10-17-15-7-6-13-15-23-14-6 1-14 4-16-4-5-3-7-11-13-13-2-9-11-14-12-22v-16c-6-5-11-11-19-14-6-3 8-2 5-9 1-10 3-22-5-30-4-9 3-18 11-22 9-3 9-14 19-15 9-5 4-18 10-25 8-5 6-15 9-22 6-6 5-16 11-21 4-8 14-1 21-2l40 3c9-3 18-4 28-6l50-4c8 2 17 5 25 3 8-1 10-11 17-10 7 5 16 10 25 10 8 6 19 9 29 6 11-3 14 10 21 16 7 7 11 17 19 24 7 3 11 10 15 15 5 9 8 19 16 26 6 7 16 11 20 20-8 2-16 5-24 5-9 2-19-2-25 5-8 1-13 7-13 15-3 10-8 18-13 26-6 7-13 15-23 15-12 1-23 4-32 11l-28 16c-7 9-13 21-24 26-9 3-17 8-15 18-3 12-14 19-24 22-7 3-13 10-21 7z"/>
+<path class="mountain" id="Huila" d="M345 1803c-7-3-16 0-11-12 0-9 1-20-3-29-3-6-5-13-10-18-5-7-14-10-17-17-2-11-7-21-5-32 9-9 8-23 17-30 10-4 8-20 19-22l22 1c8-6 9-17 15-23 5-8 14-13 15-22-1-8-2-19 6-24 5-12 18-11 28-17 14-5 22-17 30-28 5-9 8-20 7-30 0-19-5-39-19-52-8-7-8-15-6-25 1-9 3-17 6-26-1-9 1-18 12-20 8-6 2-18 9-26 1-8 10-11 7-20 8 5 16 10 26 12 14 2 30 1 43-6a64 64 0 0 0 35-53c1-10 1-21-4-30-5-12 16-3 23-10 8-4 20-7 28-1 9 0 15 3 20 10 6 7 8 18 19 20 9 4 15 12 19 21 9 7 2 20 4 30 0 12 1 25-5 36-4 11-2 24-7 35-6 3-16-2-23 4-10 2-19-3-28-6-10-1-24 2-25 14 1 11-2 21-3 31 1 10-9 17-6 27 4 8 4 20-8 19-8 2-15 5-22 10-4 9-19 3-22 10-1 18-3 36-1 53 2 9 6 16 12 23 5 7 12 13 19 17 8 1 14 6 8 14-1 10-5 17-13 22-8 6-19 8-25 16-8 5-9 15-10 24s-11 14-10 24c3 11-7 13-16 16-7 3-12 10-15 18-7 7 0 18-3 25-6 7-4 17-8 24-9 5-18 7-27 9-10 6-21 6-31 7-10 3-21 2-31 5-10 2-20-2-30 1-1 2-3 1-5 1z"/>
+<path class="grassland" id="Meta East" d="M908 1553c-10-4-4-17-8-26-1-10-6-19-12-26-1-10-6-23-18-22-10-4-14 10-21 8-4-8-3-18-8-26-9-8-22-4-30-14-9-2-17-7-25-12-4-10-21-10-20-22 3-6 14-2 19-8 9-3 16-8 22-14 7-9 14-16 17-26 2-10 6-18 5-28-1-8 1-14 10-12 9 1 21 4 27-5 8-3 14-8 20-14 4-8 10-12 16-17 6-6 2-15 6-22s8-17 13-22c12-3 6-19 15-24 8-1 16-1 23-7 7 1 12 19 23 15s18 4 25 11c8 6 12 17 23 17 9 0 20 2 20 13 6 5 12 11 20 11 10 1 20 2 29-3-1 11 3 21 0 32-1 11 0 23 3 34 1 14-2 28-1 42v100c-2 10-1 22 0 32 2 8-12 4-15 1-10-5-16 7-26 7-12-1-25 2-37-4-4-7-14-2-16 5-10 0-21 10-29 0-7-2-15-1-22-1-1 14-16 7-22 14-7 6-16 7-24 11-1 0-1 3-2 2z"/>
+<path class="grassland" id="Vichada" d="M1153 1535c-7-2-17-14-24-5-12 2-14-13-15-22s0-18 2-27v-45c-1-10-2-20-1-30 2-9 1-17-1-25 1-16 3-33 0-49-2-20-1-40-1-60 10-3 15-12 20-19 4-8 4-20 15-21 13-6 19-20 30-29 9-4 18-9 26-16s20-4 30-7c9-3 18-11 24-19 5-9 11-18 13-29 2-9 14-10 21-12 13-2 27 1 38-7 7-9 13 5 23 2 8 1 15-3 21-8 6-4 10-16 17-7 7 3 14 1 22 1 6-1 9 8 16 5 9 2 16-4 25-3 8-1 16-3 25-2 10 1 17-6 27-5 10-1 21 1 30-4 7-6 16-8 25-6 11-1 12 10 18 15 8 8 5 19-4 24-7 7-16 11-15 22 0 11 1 23-4 33-6 5-15 7-18 15-5 7-1 17-1 26-1 10 4 19 3 28-3 13-3 26-5 39-2 8-8 19-3 28 6 8 13 17 6 26-2 12 6 22 11 32-2 6-10 5-12 11-7 1-15-12-25-6-8 4-17-2-25 0-8 3-17 4-22 12-10 0-9 14-19 14-6 6-16 2-24 2-7 3-14 16-23 8-10 0-18 6-28 7-8 3-13-11-23-5-8 4-18 0-28 0-11-3-18 7-22 15-10-1-17 6-25 9-5 6-13 5-15 13-7 3-18-4-21 7-9 1-13 13-7 19-5 5-15 6-18 14-3 11-2 22-8 32-3 8-15 8-23 6-8-1-18-4-25 3z"/>
+<path class="forest" id="Narino" d="M214 1790c-10-3-17-10-28-9-10 2-12-11-21-15-1-7-3-16-11-18-9-2-17 4-23-5-8-3-15-9-24-11-7-3-11-13-16-19-8-5-5-18-15-21-5-1-16-4-8-9 7 0 17 6 25 1 6-8 15-3 23-5 9-3 7-15 9-23-3-7-5-16-10-22-7-1 0-14-6-17 6-8 8-19 17-24 7-2 11-6 18-2 9 3 13-8 19-8 12 4 17-7 26-10 6-9 17-5 25-8 5-2 7-7 14-7-2-8 4-14 9-19 7-4 10-14 10-23-7-9 4-15 9-22 11-2 6-16 14-21 8-3 17-9 17-19-1-8 9-11 14-15 5-6 14-10 12-18-1-8 12-5 14-13 7-1 15 7 23 4-1 11 13 16 8 27-8 3-14 14-24 13-6-1-10 0-8 7l-1 30c0 12-1 25 5 36 6 13 17 24 30 31 4 5 22 3 11 9-6 10-4 22-8 33-5 5-8 12-17 8-11-1-14 10-19 17-8 2-13 8-18 13 0 11-13 14-16 23-4 8-4 16-11 23-6 12-4 25-1 38 2 10-15 3-22 6-9 2-17 7-24 12-8 5-22 1-19 15-1 9 2 19-2 27z"/>
+<path class="forest" id="Guainia" d="M1274 1675c-8-5-18-5-26-9-12-3-1-15-4-24 3-9 18-11 15-24 1-11-13-15-22-14-8 2-17 8-26 2-10-2-24 5-30-6-7-5-9-17-17-24-11-3 4-10 8-13 10-4 17-12 26-15 8-4 19-6 21-17 6-9 2-21 6-31 1-5 13-2 14-10 7-4 5-14 6-19 6-6 20 3 22-7 6-2 3-10 10-10 8-6 18-10 29-10 9-1 6-18 17-13 10 1 19 1 28-2 8 4 18 7 27 5 10-1 18-6 27-3 9-4 19-6 27-12 9 0 20 1 27-6 7-4 12-10 18-15 7-4 14-11 23-5 9 0 18-4 25 3 5 8 20 3 23-5 5-3 10 7 10 12 3 7 5 16 13 19 4 6 15 1 13 9v46c-7 5-15 10-20 18-6 7-15 10-22 16-7 5-12 13-10 22 1 11 11 16 20 14 5 6 17 0 18 11 5 8 17 10 14 22v41c-9 6-17 13-24 21-7 6-12 14-17 21-4 2-12 13-15 5-6-6-6-15-11-22-7-5-18-7-27-3-10 1-10 12-12 20 1 7 1 12-7 10h-120c-7 1-14-3-21-3-8-2-15-8-24-4-6 6-15 1-22 6z"/>
+<path class="forest" id="Meta West" d="M728 1692c-10-2-16-12-23-19-8-4-15-9-24-10-9 4-16-4-23-9-6-6-10-14-9-23 1-10-3-19-5-28l-5-29c6-7 7-17 10-26 2-10-4-19-5-29-2-9-10-13-15-20-5-8-21-8-18-19 0-8 8-15 5-25-4-7-4-20 4-24 7-5 11-10 21-7 7 3 16 6 24 2 8-2 16-4 21-9 10-8 10-23 10-34 7 8 15 14 23 19 8 3 15 5 21 9 11-1 0 17 10 19 0 11 16 8 18 17 3 5 9 3 11 10 7 2 12 12 21 8 10 2 5 16 13 22 4 7 17 0 15 11 4 11 18 10 27 10 6 0 12-5 14-5-2 8 5 15 9 20 1 10 5 19 3 30-9 2-7 14-17 16-4 11-16 13-26 14-6 5-15 1-21 6-8 4-19-2-29-1-9-1-22-2-21 11-3 11-3 21-1 32 0 11-6 23-2 34 1 8 3 23-10 21-8 4-17 5-26 6z"/>
+<path class="forest" id="Guaviare" d="M986 1813c-8-9-23-1-28-12-5-7-10-12-11-20-6-5-6-17-17-17-8-7-18-11-27-17-10-6-16 4-26 2-12 0-11 13-14 20-6 4-13 12-20 11-6-9-19-9-24-19-11-5-13-17-15-27-5-7-10-14-18-17-3-7-8-15-8-23 0-16-1-32 1-48 3-14 0-28 2-41 4-4 16 0 22-1 8-1 17 2 25-2 5-4 14 0 18-7 12 0 24-4 31-14 5 9 16 8 25 6 11-2 7-16 14-20 6-3 6-9 13-9 10-6 19-9 30-10 6-2 9-14 15-7 6 11 22 3 31 0 10 1 17-8 27-2 9 1 19-1 29-1 7 4 14-1 18-4 7 8 15 0 23 3 8-1 14 8 23 6 8-1 15 1 22 4 9 2 18 1 25-4 4 2-8 6-11 8-10 5-19 13-20 24-2 9 5 11 12 13 7 7 9 18 16 24 6 5 10 12 19 9s17-1 26 1c9 0 19 1 26-4 7 4-9 7-9 13l-26 10c-11-2-19 6-29 8-17 3-35-2-51 6-9 3-11 15-22 13-9 4-17 12-23 20-7 9-16 16-24 24-4 7-11 14-11 23 1 9-1 18-7 25-8 5-10 16-20 14-7 6-9 16-16 23-6 5-12 8-16 16z"/>
+<path class="forest" id="Vaupes" d="M1318 2055c-9 1-5-12-14-14-7-10-17 1-25-2-8 3-4-11-13-10-9 2-23-2-28 7 0 6-5-1-9-1 9-9-1-20 2-29 0-9 2-20-4-28-2-10-15-11-24-13-5-4-6-13-15-12-9-3-18-4-26-9-7-6-17 9-17-2-2-7-6-14-5-22 0-7-7-13-14-14-11 0-15-12-25-14-10 1-17-5-24-10-10-2-20 1-28-6-9-4-16-8-23-16-5-7-10-16-18-19 1-8-7-10-12-11 1-10 10-16 17-22 9-5 8-18 18-21 9-2 13-12 19-18 7-8 11-20 9-30 3-8 9-13 13-20 7-5 12-12 19-19 6-8 13-17 24-19 11 0 12-13 23-15 9-5 18 1 27-1 8-3 18-2 26-6 9-6 22-2 31-8 5-3 10-2 4 3-4 9 3 19 11 24 7 7 19 4 26 10 4 6 1 16 2 23 0 21 0 42 2 64 4 11 18 3 26 4 12-1 24 3 36 3 6 5 20-3 17 9v23c-7-2-10-11-19-11-8 0-15 4-20 8-9 0-18 3-25 8-11 6-24 0-36 4-14 5-6 20-7 31 0 17-5 34-2 51 0 11-2 22 2 33 4 9 13 13 15 23 5 6 18-1 20 9 2 11 14 11 22 14 6 6 3 14-1 20-3 9 1 18 9 22 4 6 5 14 12 18 6 2 3 6 2 11z"/>
+<path class="forest" id="Putumayo" d="M912 1993c-9-3-10-17-22-13-8-2-17 8-23-2-7-5-19-4-25 2-7 13-16-4-24-7-10-3-20 1-29 1-6-5-11-8-16-14-9-4-20-2-30-2-11 0-20 8-31 9-11 4-22 10-33 12-5-9-16-6-20-16-7-4-13-7-11-16v-31h-58c-7-6-17-12-27-11-9-3-9 14-16 6-8-3-17-4-21-11-8-5-18-2-27-5-9-1-15-8-22-13-8-4-10-17-20-22-8-2-11-11-20-10-10 0-19-5-29-2-7 1-18 0-16 11 2 9-7 15-14 8-9-4-19-5-27 0-8-1-19-1-24-8-6-7-17-7-25-4l-24-7c-5-1-7-10 1-7 9 3 16-4 25-7 7-4 12-11 19-14 11 0 21 5 33 2 8-3 17 1 25-1l24-3c8-4 18 0 26-4 11-1 20-7 31-8 9-4 22-1 28-11 8-7 3-18 6-27 5-9 1-19 5-27 3-12 17-9 23-17 5-8 17-8 22-15 3-8 6-20-3-26-4-5-1-17 6-20 6-8 18-8 25-16 9-7 12-16 15-26 4-6 5-14 8-20 10-2 21-4 28-12 5 4 2 16 4 23 3 8 8 16 6 25-3 9 4 15 7 23 4 9 15 10 21 17 9 5 20-1 28 6 9 7 17 16 27 22 10 4 20 0 30-1 7 0 17-9 16 2 3 8 6 16 11 23 8 5 17 11 18 21 0 13 11 19 19 27 8 6 17 10 25 17 7 4 20 5 24-5 9-4 20-11 18-23 4-5 15-3 19-4 5 8 16 7 22 14 9 2 11 12 16 18 0 8 9 13 12 21 6 9 19 4 26 9 12 2 3 21 17 20 6 2 2 16 13 14 7 2 6 15 15 17 5 8 15 10 23 15 11 5-8 13-10 20-4 9-13 14-19 21-4 4-8 9-14 8h-15c-6 5-11 11-19 14-3 9-13 14-14 24 1 4 1 13-5 6-10-7-23 0-31 7-1 1-2 4-3 3z"/>
+<path class="forest" id="Amazonas" d="M1255 2451c-8-5-11-13-14-21-7-5-10-13-18-18-3-10-15-8-24-8-7 3-15 12-22 3-3-5 7-13 10-19 4-7 9-12 12-20 5-8 7-17 13-23l16-24c3-6 7-11 13-17 4-6 5-14 11-19 4-8 5-19 2-28-7-6-18 1-21-10-7-5-11-12-18-15-8-2-16 3-21-5-7-6-17 0-21-9-7-6-13-14-23-15-3-11-16-7-24-7-10 1-14 11-24 12-5 7-18 5-24 6-6-7-15-8-21-15-9-2-20-1-27-8-9-4-17 3-24 7-6 5-6 14-15 17-10-2-17 6-24 10-8-3-12 7-19 4-5-4-15-2-22-3-8 1-17-7-26-2-6 6-18 1-25 7-4-10-15-12-23-14-8-8 5-16 2-25v-32l-17-18c-8-6-17 3-24 4-6 1-5-8-11-8 4-9 3-20-2-28-3-10-7-19-14-26-6-9-16-11-25-15-7 0-9-1-11-8-4-9-18-4-21-14-3-8-13-4-20-7-12 1-15-11-14-20-3-7-9-12-9-21-6-10 12-7 17-12 9-3 17-8 27-9 9-5 18-6 28-7 8 0 9 10 17 12 6 8 18 6 27 5 10-7 14 7 22 10 8 1 19 3 26-2 3-7 8-8 11-1 8 5 18 4 26 0 7-3 8 11 16 11 7 4 18 5 23-3 7-11 16-2 26-3 9 1 16-9 14-17 0-8 9-12 12-19 8-4 14-15 24-9 11 0 19-10 27-17 6-8 16-12 19-22 7-6 12-19 23-19 6 5 13 11 22 9 9 0 14 13 24 13 13 0 1 14 10 20 5 7 4 21 15 22 8 2 15-4 22 1 9 3 22 2 26 13 4 10 18 2 22 11 9 7 2 16 0 25 1 8 8 13 2 20-4 11 5 17 13 21 4 9 18 9 24 3 3-5 1-13 8-10-2 12 12 10 20 11 5 2 11 0 15-2 6 6 5 17 16 15 8 2 19 0 15 11 1 9-7 16-8 25l-9 51c-3 15-4 29-8 44-1 14-3 28-8 41-5 7-4 17-5 26-2 16-5 33-9 49-2 17-3 34-8 50l-7 33c-1 8-4 16-4 24z"/>
+<path class="foreign" id="Panama" d="M211 926c-5-7-9-14-17-18-7-7-8-18-17-24-3-8-5-17-11-25-4-5-12-14-1-15 8-3 15-10 15-19-1-7-3-13 6-13 5 7 13 10 21 11 10 3 20-2 19-13 0-13-16-9-22-18-4-9-10-20-22-21-6-4-15-4-21-3-4-8-18-2-18-14-4-10-14-14-23-19-5-7-16-4-19-13-4-6-7-12-15-13-8-3-14-10-22-12-14-5-29-3-43-5-6-2-20 6-18-3v-47c10-2 20 1 30 4 10 1 25-4 23 11 11 0 21 2 31 4 11 0 23-1 34 1 10 3 21 4 30 12 11 5 22 9 33 10 8 3 15 6 19 13l19 17c4 6 7 13 13 18 4 7 10 12 18 15 2 7 8 15 12 19-9 6-5 20 1 27 12 2 6 16 12 22 8 5 13 12 15 22 0 5 5 10-3 11-6 7-16 12-16 22-2 6 6 13-3 11-5 1-8 13-11 5-8-3-10-12-17-16-9-2-18 2-20 11-3 7-2 16 2 21-10 3-13 15-14 24z"/>
+<path class="foreign" id="Venezuela" d="M1356 1097c-8-7-18-6-24-15l-23-23c-2-7-7-14-11-19-2-5-9-11 0-9l260 1h26v42c-7-6-18-5-27-8-10 0-22 2-30 7-5 9-18 2-26 4-10-2-18 8-27 7-9-6-19 0-28 2-9 1-16 3-25-1-11 1-21-3-31-7-10-1-18 4-26 10-3 3-4 7-8 9zm-70-71c-7-6-13-14-18-22-7-7-11-16-20-22-7-4-17-5-24 0-7-1-15-6-22-8-11-4-20-12-32-10-4-8-13-11-22-10-9-1-16 5-25 6l-58 5c-9 0-18-1-26 3-10-1-22 2-29-5-6-6-16-6-22-11-11-7-19-19-32-25 12-3 24-6 33-14 16-11 25-31 23-51 0-19-8-39-26-50-8-6-18-8-28-9-11-1-14-13-15-22-6-9-12-17-16-27-4-8-8-16-9-25-4-9-12-16-17-24-7-8-15-15-25-17-5-4-4-20-16-17-6-1-16 4-18 4 4-9 7-19 16-24 6-6 2-17 10-22 8-6 11-14 9-24-4-8 0-19-1-28 3-14 10-28 9-42 4-9 13-14 16-24 5-10 10-22 17-32 9-6 11-17 18-25 4-10 16-4 23-10 6-4 15-5 18-12 3-6 4-18 8-21v160c0 12-6 23-13 31-7 10 0 22 6 31 8 6 9 14 8 23v156h300z"/>
+<path class="foreign" id="Ecuador" d="M3 1848v-68c9-2 18-5 27-5 10 0 16-12 24-11 12 5 15-9 23-13 6 0 7-14 11-5 9 6 20 9 30 15 10 3 8 14 15 21 6 6 17 0 23 5 6 8 13 15 24 15 10-1 15 7 25 9 10 1 18 4 22 14 6 8 15 12 24 17 7 7 3 21 14 25l29 8c9 0 18-3 24 6 9 2 17 6 27 7 9 2 18-1 27-1 8 5 18 5 27 2 10 0 15-10 13-19 10-5 21 0 30 6 8 4 11 12 16 18l21 17c4 2 16 6 6 5H3Z"/>
+<path class="foreign" id="Brasil" d="M1338 1764c-8-4-17-3-26-4-8 0-16-5-25-2 0-21-2-42-1-63 11-3 22-2 32-7 9 4 19 3 28 6v69zm-66 715c3-9 4-19 5-28 2-14 3-28 8-41 3-13 2-26 7-39 3-13 5-27 6-42 2-11 3-23 7-34 3-9 4-19 4-29 2-12 7-22 8-34 3-15 3-31 7-46 2-12 4-25 8-37 0-10 2-20 5-29 0-9 4-15 9-23v382zm74-417c-5-9-4-19-9-28-7-6-12-15-17-23-1-11 5-23-2-34-3-8-11-12-19-14-8-5-13-13-21-17-9-3-14-12-20-18l1-81c2-10 18-2 25-6 10-5 20-7 31-9 6-5 11-7 17-1 5 4 16 4 14 13z"/>
+<path class="foreign" id="Peru" d="M1014 2479v-220H648v-277c7 8 18 11 19 23 0 8 5 16 7 22-4 9 5 17 9 25 5 6 14 7 21 9 10 1 16 10 26 13 7 5 10 17 21 17 8 1 16 3 21 10 6 9 12 21 11 31-4 10-3 20 7 25 6 10 18 11 28 10 8-4 16-1 16 9 0 8 2 16-2 23-6 11-1 24 4 34 6 8 19 3 23 13 6 7 17 5 26 4 7-4 15 5 22-1 9-4 22-3 32 0 11 6 26 3 37-3 9-1 14-12 25-10 10-1 15-11 20-18 5-6 16 1 24 1 4 7 11 10 19 12 7 9 19 4 29 4 8 0 17-1 23-7 9-1 14-11 21-8 9 3 14 9 21 14 5 6 8 15 17 17 8-2 15-2 22 3 8-1 17 2 21 10 2 8 19 9 10 17-2 9-9 16-14 23l-16 25c-9 8-12 21-17 31-5 7-12 14-16 22l-14 20c-4 9 2 18 10 23 9 3 17 7 26 8s17-13 22-1c9 5 14 14 18 24 1 4 16 10 5 8-5-1-10 8-10-2-2-11-14-12-22-11-11-2-22 0-32-3-9 1-19 4-28 7-6 6-12 14-16 21-10 6-26 2-38 3z"/>
+</svg>
+
+<div id="cities">
+
+<div id="Bogota" class="box city" style="top:1266px;left:684px;width:141px;height:140px"></div>
+<div id="Cali" class="box city" style="top:1434px;left:328px;width:126px;height:126px"></div>
+<div id="Medellin" class="box city" style="top:1003px;left:450px;width:126px;height:125px"></div>
+<div id="Bucaramanga" class="box city" style="top:920px;left:779px;width:120px;height:120px"></div>
+<div id="Cartagena" class="box city" style="top:457px;left:445px;width:110px;height:110px"></div>
+<div id="Cucuta" class="box city" style="top:809px;left:896px;width:110px;height:110px"></div>
+<div id="Ibague" class="box city" style="top:1219px;left:447px;width:120px;height:119px"></div>
+<div id="Neiva" class="box city" style="top:1488px;left:534px;width:110px;height:109px"></div>
+<div id="Pasto" class="box city" style="top:1728px;left:219px;width:110px;height:110px"></div>
+<div id="Santa Marta" class="box city" style="top:376px;left:622px;width:120px;height:119px"></div>
+<div id="Sincelejo" class="box city" style="top:653px;left:459px;width:110px;height:110px"></div>
+
+</div>
+
+<div id="boxes">
+<div id="PRES1" class="box president" style="top:129px;left:240px;width:76px;height:168px"></div>
+<div id="PRES2" class="box president" style="top:129px;left:322px;width:78px;height:168px"></div>
+<div id="PRES3" class="box president" style="top:129px;left:406px;width:76px;height:168px"></div>
+
+<div id="SOP_A1" class="box" style="top:438px;left:1261px;width:74px;height:74px"></div>
+<div id="SOP_A2" class="box" style="top:438px;left:1336px;width:74px;height:74px"></div>
+<div id="SOP_B1" class="box" style="top:516px;left:1261px;width:74px;height:74px"></div>
+<div id="SOP_B2" class="box" style="top:516px;left:1336px;width:74px;height:74px"></div>
+<div id="SOP_C1" class="box" style="top:595px;left:1261px;width:74px;height:74px"></div>
+<div id="SOP_C2" class="box" style="top:595px;left:1336px;width:74px;height:74px"></div>
+<div id="SOP_PASS" class="box" style="top:663px;left:1112px;width:105px;height:49px"></div>
+
+<div id="IM1" class="box" style="top:1935px;left:14px;width:196px;height:274px"></div>
+<div id="IM2" class="box" style="top:1935px;left:220px;width:196px;height:274px"></div>
+
+<div id="GOVT_CAPS" class="box" style="top:461px;left:224px;width:207px;height:149px"></div>
+<div id="GOVT_BASES" class="box" style="top:371px;left:287px;width:177px;height:55px"></div>
+<div id="CARTELS_SHIPMENTS" class="box" style="top:1728px;left:1391px;width:145px;height:125px"></div>
+<div id="CARTELS_BASES" class="box" style="top:2117px;left:1373px;width:183px;height:305px"></div>
+<div id="AUC_BASES" class="box" style="top:2386px;left:446px;width:360px;height:55px"></div>
+<div id="FARC_BASES" class="box" style="top:2295px;left:446px;width:543px;height:56px"></div>
+
+</div>
+
+<div id="tokens">
+<div class="token" id="token_aid"></div>
+<div class="token" id="token_oppose_plus_bases"></div>
+<div class="token" id="token_total_support"></div>
+<div class="token" id="token_el_presidente" style="left:254px;top:245px"></div>
+<div class="token" id="token_prop_card" style="left:1029px;top:666px"></div>
+</div>
+
+<div id="pieces">
+<div id="govt_cylinder" class="piece govt cylinder"></div>
+<div id="auc_cylinder" class="piece auc cylinder"></div>
+<div id="cartels_cylinder" class="piece cartels cylinder"></div>
+<div id="farc_cylinder" class="piece farc cylinder"></div>
+<div id="govt_resources" class="piece govt cylinder"></div>
+<div id="auc_resources" class="piece auc cylinder"></div>
+<div id="cartels_resources" class="piece cartels cylinder"></div>
+<div id="farc_resources" class="piece farc cylinder"></div>
+</div>
+
+</div>
+</div>
+
+<div id="card_panel">
+<div id="deck_outer" class="card card_back"><div id="deck_size"></div></div>
+<div id="next_card" class="card card_2"></div>
+<div id="this_card" class="card card_1"></div>
+</div>
+
+</main>
+
+<footer id="status"></footer>
+
+</body>
diff --git a/play.js b/play.js
new file mode 100644
index 0000000..89df679
--- /dev/null
+++ b/play.js
@@ -0,0 +1,822 @@
+// TODO: FARC zone
+// TODO: terror markers
+
+// Spaces
+const AVAILABLE = -1
+
+// Sequence of Play options
+const ELIGIBLE = 0
+const SOP_A1 = 1
+const SOP_A2 = 2
+const SOP_B1 = 3
+const SOP_B2 = 4
+const SOP_C1 = 5
+const SOP_C2 = 6
+const SOP_PASS = 7
+const INELIGIBLE = 8
+
+let ui = {
+ favicon: document.getElementById("favicon"),
+ header: document.querySelector("header"),
+ player: {
+ govt: document.getElementById("role_Government"),
+ auc: document.getElementById("role_AUC"),
+ cartels: document.getElementById("role_Cartels"),
+ farc: document.getElementById("role_FARC"),
+ },
+ spaces: [],
+ control: [],
+ support: [],
+ sabotage: [],
+ next_card: document.getElementById("next_card"),
+ this_card: document.getElementById("this_card"),
+ deck_size: document.getElementById("deck_size"),
+ sop: [
+ null,
+ document.getElementById("SOP_A1"),
+ document.getElementById("SOP_A2"),
+ document.getElementById("SOP_B1"),
+ document.getElementById("SOP_B2"),
+ document.getElementById("SOP_C1"),
+ document.getElementById("SOP_C2"),
+ document.getElementById("SOP_PASS"),
+ ],
+ misc: {
+ aid: document.getElementById("token_aid"),
+ total_support: document.getElementById("token_total_support"),
+ oppose_plus_bases: document.getElementById("token_oppose_plus_bases"),
+ president: document.getElementById("token_el_presidente"),
+ propaganda: document.getElementById("token_prop_card"),
+ shipments: [],
+ },
+ govt: {
+ resources: document.getElementById("govt_resources"),
+ cylinder: document.getElementById("govt_cylinder"),
+ police: [],
+ troops: [],
+ bases: [],
+ },
+ auc: {
+ resources: document.getElementById("auc_resources"),
+ cylinder: document.getElementById("auc_cylinder"),
+ guerrillas: [],
+ bases: [],
+ },
+ cartels: {
+ resources: document.getElementById("cartels_resources"),
+ cylinder: document.getElementById("cartels_cylinder"),
+ guerrillas: [],
+ bases: [],
+ },
+ farc: {
+ resources: document.getElementById("farc_resources"),
+ cylinder: document.getElementById("farc_cylinder"),
+ guerrillas: [],
+ bases: [],
+ },
+}
+
+function create(t, p, ...c) {
+ let e = document.createElement(t)
+ Object.assign(e, p)
+ e.append(c)
+ return e
+}
+
+function register_action(e, action, id) {
+ e.my_action = action
+ e.my_id = id
+ e.onclick = on_click_action
+}
+
+function is_action(action, arg) {
+ if (arg === undefined)
+ return !!(view.actions && view.actions[action] === 1)
+ return !!(view.actions && view.actions[action] && set_has(view.actions[action], arg))
+}
+
+function toggle_pieces() {
+ document.getElementById("pieces").classList.toggle("hide")
+}
+
+function on_click_action(evt) {
+ if (evt.button === 0)
+ send_action(evt.target.my_action, evt.target.my_id)
+}
+
+const first_pop = 1
+const first_city = 0
+const last_city = 10
+const first_dept = 11
+const last_pop = 22
+const last_dept = 26
+const first_foreign = 27
+const last_foreign = 31
+const first_loc = 32
+const last_loc = 49
+
+const center_xy = {
+ "Santa Marta": [682,436],
+ "Cartagena": [500,512],
+ "Sincelejo": [514,708],
+ "Medellin": [513,1066],
+ "Ibague": [507,1278],
+ "Cali": [391,1497],
+ "Pasto": [274,1783],
+ "Neiva": [589,1542],
+ "Bogota": [754,1336],
+ "Bucaramanga": [839,980],
+ "Cucuta": [951,864],
+ "Guainia": [1409,1542],
+ "Vaupes": [1167,1785],
+ "Amazonas": [1085,2064],
+ "Vichada": [1340,1274],
+ "Ecuador": [94,1792],
+ "Panama": [188,686],
+ "Cesar": [802,533],
+ "Atlantico": [646,642],
+ "Antioquia": [666,910],
+ "Choco": [373,1040],
+ "Narino": [212,1642],
+ "Huila": [457,1628],
+ "Santander": [818,1177],
+ "Arauca": [1092,1128],
+ "Meta East": [973,1410],
+ "Meta West": [720,1539],
+ "Guaviare": [976,1669],
+ "Putumayo": [680,1826],
+ "Sincelejo / Ayacucho": [642,696],
+ "Cucuta / Arauca": [994,977],
+ "Bucaramanga / Ibague / Bogota": [626,1224],
+ "Bogota / Yopal": [887,1276],
+ "Bogota / Neiva": [612,1414],
+ "Bogota / San Jose": [826,1474],
+ "Neiva / Pasto": [530,1698],
+ "Pasto / Tumaco": [146,1766],
+ "Cali / Pasto": [348,1625],
+ "Cali / Buenaventura": [368,1412],
+ "Ibague / Cali": [436,1362],
+ "Medellin / Ibague": [511,1169],
+ "Cartagena / Sincelejo": [514,613],
+ "Sincelejo / Medellin": [563,876],
+ "Bucaramanga / Ayacucho": [778,830],
+ "Cucuta / Ayacucho": [872,708],
+ "Santa Marta / Ayacucho": [746,601],
+ "Santa Marta / Cartagena": [588,466],
+ // TODO:
+ "Brasil": [10, 10],
+ "Peru": [30, 10],
+ "Venezuela": [40, 10],
+}
+
+const layout_xy = {
+ "Atlantico": [620,540],
+ "Choco": [370,1190],
+ "Narino": [260,1530],
+
+ "Cesar": [860,420],
+ "Antioquia": [640,1010],
+ "Santander": [710,1175],
+ "Huila": [525,1425],
+
+ "Arauca": [1210,1090],
+ "Vichada": [1210,1305],
+ "Meta East": [971,1320],
+
+ "Meta West": [720,1630],
+ "Guaviare": [860,1670],
+ "Putumayo": [840,1880],
+
+ "Guainia": [1310,1540],
+ "Vaupes": [1080,1810],
+ "Amazonas": [950,2080],
+
+ "Ecuador": [190,1870],
+ "Panama": [235,810],
+ "Brasil": [1300, 1900],
+ "Peru": [720, 2170],
+ "Venezuela": [1130, 880],
+}
+
+function get_center_xy(s) {
+ let id = data.spaces[s].id
+ return center_xy[id]
+}
+
+function get_layout_xy(s) {
+ let id = data.spaces[s].id
+ if (layout_xy[id])
+ return layout_xy[id]
+ return center_xy[id]
+}
+
+function get_layout_radius(s) {
+ switch (data.spaces[s].pop) {
+ case 0: return 0
+ case 1: return 53
+ case 2: return 57
+ case 3: return 61
+ case 8: return 68
+ }
+}
+
+function init_ui() {
+ for (let i = 0; i < data.spaces.length; ++i) {
+ let id = data.spaces[i].id
+ let type = data.spaces[i].type
+ let e = null
+ if (type === "road" || type === "pipeline") {
+ e = document.createElement("div")
+ let [ x, y ] = center_xy[id]
+ e.className = "box loc"
+ e.style.left = x - 28 + "px"
+ e.style.top = y - 28 + "px"
+ e.style.width = 56 + "px"
+ e.style.height = 56 + "px"
+ document.getElementById("boxes").appendChild(e)
+ } else if (type === "city") {
+ e = document.getElementById(id)
+ } else {
+ e = document.getElementById("svgmap").getElementById(id)
+ }
+ if (!e)
+ console.log("MISSING SPACE", id)
+ else {
+ ui.spaces[i] = e
+ register_action(e, "space", i)
+ }
+
+ if (i <= last_pop) {
+ let [x, y] = center_xy[id]
+ if (i <= last_city)
+ ui.support[i] = e = create("div", { className: "hide" })
+ else
+ ui.support[i] = e = create("div", { className: "hide" })
+ if (i <= last_city) {
+ let r = get_layout_radius(i)
+ e.style.left = (x - 20) + "px"
+ e.style.top = (y - 20 - r) + "px"
+ } else {
+ e.style.left = (x + 14) + "px"
+ e.style.top = (y - 45) + "px"
+ }
+ document.getElementById("tokens").appendChild(e)
+ }
+
+ if (i <= last_dept || id === "Panama" || id === "Ecuador") {
+ let [x, y] = center_xy[id]
+ if (i <= last_city)
+ ui.control[i] = e = create("div", { className: "token govt_control" })
+ else
+ ui.control[i] = e = create("div", { className: "hide" })
+ if (i <= last_city) {
+ let r = get_layout_radius(i)
+ e.style.left = (x - 25 + r) + "px"
+ e.style.top = (y - 25) + "px"
+ } else if (i > last_pop) {
+ e.style.left = (x - 25) + "px"
+ e.style.top = (y - 25) + "px"
+ } else {
+ e.style.left = (x - 57) + "px"
+ e.style.top = (y - 49) + "px"
+ }
+ document.getElementById("tokens").appendChild(e)
+ }
+
+ if (i >= first_loc && i <= last_loc) {
+ let [x, y] = center_xy[id]
+ ui.sabotage[i] = e = create("div", { className: "hide" })
+ e.style.left = (x - 20) + "px"
+ e.style.top = (y - 20) + "px"
+ document.getElementById("tokens").appendChild(e)
+ }
+ }
+
+ function create_piece(c, action, id, x, y) {
+ let e = create("div", {
+ className: c,
+ my_action: action,
+ my_id: id,
+ my_x_offset: x,
+ my_y_offset: y,
+ onclick: on_click_action
+ })
+ document.getElementById("pieces").appendChild(e)
+ return e
+ }
+
+ function create_piece_list(list, c, action, n, x, y) {
+ for (let i = 0; i < n; ++i)
+ list[i] = create_piece(c, action, i, x, y)
+ }
+
+ create_piece_list(ui.misc.shipments, "token shipment", "shipment", 4, 0, 0)
+
+ create_piece_list(ui.govt.police, "piece govt police", "govt_police", 30, 0, 4)
+ create_piece_list(ui.govt.troops, "piece govt troops", "govt_troops", 30, 0, 4)
+ create_piece_list(ui.govt.bases, "piece govt base", "govt_base", 3, -4, 10)
+
+ create_piece_list(ui.auc.guerrillas, "piece auc guerrilla", "auc_guerrilla", 18, 2, 0)
+ create_piece_list(ui.auc.bases, "piece auc base", "auc_base", 6, -4, 10)
+
+ create_piece_list(ui.cartels.guerrillas, "piece cartels guerrilla", "cartels_guerrilla", 12, 2, 0)
+ create_piece_list(ui.cartels.bases, "piece cartels base", "cartels_base", 15, -4, 10)
+
+ create_piece_list(ui.farc.guerrillas, "piece farc guerrilla", "farc_guerrilla", 30, 2, 0)
+ create_piece_list(ui.farc.bases, "piece farc base", "farc_base", 9, -4, 10)
+
+ register_action(ui.sop[1], "sop", 1)
+ register_action(ui.sop[2], "sop", 2)
+ register_action(ui.sop[3], "sop", 3)
+ register_action(ui.sop[4], "sop", 4)
+ register_action(ui.sop[5], "sop", 5)
+ register_action(ui.sop[6], "sop", 6)
+ register_action(ui.sop[7], "sop", 7)
+}
+
+function filter_piece_list(list, slist, elist, space) {
+ for (let i = 0; i < slist.length; ++i)
+ if (slist[i] === space)
+ list.push(elist[i])
+}
+
+function layout_cubes_available(slist, elist, space, xorig, yorig) {
+ let list = []
+ filter_piece_list(list, slist, elist, space)
+ layout_pieces(list, xorig, yorig)
+}
+
+function layout_guerrillas_available(slist, elist, space, xorig, yorig) {
+ let list = []
+ filter_piece_list(list, slist, elist, space)
+ layout_pieces(list, xorig, yorig)
+}
+
+function layout_pieces(list, xorig, yorig) {
+ const dx = 17
+ const dy = 11
+ if (list.length > 0) {
+ let ncol = Math.round(Math.sqrt(list.length))
+ let nrow = Math.ceil(list.length / ncol)
+ function layout_piece(row, col, e, z) {
+ // basic piece size = 29x36
+ let x = xorig - (row * dx - col * dx) - 15 + (nrow-ncol) * 6
+ let y = yorig - (row * dy + col * dy) - 24 + (nrow-1) * 8
+ let xo = e.my_x_offset
+ let yo = e.my_y_offset
+ e.style.left = (xo + x) + "px"
+ e.style.top = (yo + y) + "px"
+ e.style.zIndex = z
+ }
+ let z = 50
+ let i = 0
+ for (let row = 0; row < nrow; ++row)
+ for (let col = 0; col < ncol && i < list.length; ++col)
+ layout_piece(row, col, list[list.length-(++i)], z--)
+ }
+}
+
+function place_piece(p, x, y, z) {
+ p.style.top = y + "px"
+ p.style.left = x + "px"
+ if (z)
+ p.style.zIndex = z
+}
+
+function layout_space_bases(list, xc, yc, r) {
+ // base is 44x38
+ if (r > 0) {
+ let a = 45 * Math.PI / 180
+ let dx = Math.round((r) * Math.cos(a))
+ let dy = Math.round((r) * Math.sin(a))
+ if (list.length > 0)
+ place_piece(list[0], xc - 22 + dx, yc - 19 + dy)
+ if (list.length > 1)
+ place_piece(list[1], xc - 22 - dx, yc - 19 + dy)
+ } else {
+ if (list.length > 0)
+ place_piece(list[0], xc - 20 - 34, yc + 13)
+ if (list.length > 1)
+ place_piece(list[1], xc - 20 + 31, yc + 13)
+ }
+}
+
+function layout_available_bases(list, x0, y0, cols, rows, dx, dy) {
+ let x = x0
+ let y = y0
+ // for (let i = list.length-1; i >= 0; --i) {
+ for (let i = 0; i < list.length; ++i) {
+ place_piece(list[list.length-i-1], x - 44 - 6, y + 8)
+ y += dy
+ if (i % rows === rows - 1) {
+ x -= dx
+ y = y0
+ }
+ }
+}
+
+const sop_xy = [
+ [SOP_A1, 1298-22, 475-24],
+ [SOP_A2, 1374-22, 475-24],
+ [SOP_B1, 1298-22, 554-24],
+ [SOP_B2, 1374-22, 554-24],
+ [SOP_C1, 1298-22, 632-24],
+ [SOP_C2, 1374-22, 632-24],
+]
+
+const SHORT_LIST = [ "govt", "auc", "cartels", "farc" ]
+const SHORT = {
+ "Government": "govt",
+ "AUC": "auc",
+ "Cartels": "cartels",
+ "FARC": "farc",
+}
+
+function layout_sop() {
+ let x, y, z
+
+ // Eligible
+ x = 1164 - 22
+ y = 480
+ z = 1
+ let order = data.cards[view.deck[0]].order
+ if (!order)
+ order = [ "Government", "AUC", "Cartels", "FARC" ]
+ for (let faction of order) {
+ faction = SHORT[faction]
+ if (view[faction].cylinder === ELIGIBLE) {
+ place_piece(ui[faction].cylinder, x, y, z)
+ y += 40
+ z += 1
+ }
+ }
+
+ // Ineligible
+ x = 1510 - 22
+ y = 480
+ z = 1
+ for (let faction of SHORT_LIST) {
+ if (view[faction].cylinder === INELIGIBLE) {
+ place_piece(ui[faction].cylinder, x, y, z)
+ y += 40
+ z += 1
+ }
+ }
+
+ // Pass
+ x = 1164 - 22 - 24
+ y = 688 - 28
+ z = 1
+ i = 0
+ for (let faction of SHORT_LIST) {
+ if (view[faction].cylinder === SOP_PASS) {
+ place_piece(ui[faction].cylinder, x, y, z)
+ x += 48
+ z += 1
+ if (++i === 2) { x -= 72; y += 28 }
+ }
+ }
+
+ for (let [i, x, y] of sop_xy) {
+ if (view.govt.cylinder === i) place_piece(ui.govt.cylinder, x, y)
+ if (view.auc.cylinder === i) place_piece(ui.auc.cylinder, x, y)
+ if (view.cartels.cylinder === i) place_piece(ui.cartels.cylinder, x, y)
+ if (view.farc.cylinder === i) place_piece(ui.farc.cylinder, x, y)
+ }
+
+ for (let i = 1; i <= 7; ++i) {
+ if (view.actions && view.actions.sop && set_has(view.actions.sop, i))
+ ui.sop[i].classList.add("action")
+ else
+ ui.sop[i].classList.remove("action")
+ }
+}
+
+function calc_oppose_bases() {
+ let total = 0
+ for (let s = 0; s <= last_pop; ++s) {
+ if (view.misc.support[s] < 0)
+ total -= data.spaces[s].pop * view.misc.support[s]
+ }
+ for (let b of view.farc.bases)
+ if (b !== AVAILABLE)
+ total += 1
+ return total
+}
+
+function calc_support() {
+ let total = 0
+ for (let s = 0; s <= last_pop; ++s) {
+ if (view.misc.support[s] > 0)
+ total += data.spaces[s].pop * view.misc.support[s]
+ }
+ return total
+}
+
+function layout_score_cell(list, x, y, dx, dy) {
+ let z = 1
+ if (list.length > 1) {
+ if (dy > 0) y -= 12
+ if (dy < 0) y += 12
+ if (dx > 0) x -= 12
+ if (dx < 0) x += 12
+ }
+ for (let p of list) {
+ if (p.classList.contains("token"))
+ place_piece(p, x - 24, y - 24, z)
+ else
+ place_piece(p, x - 22, y - 24, z)
+ x += dx
+ y += dy
+ z += 1
+ }
+}
+
+function layout_score() {
+ let list = []
+ let x, y
+ for (let i = 0; i <= 99; ++i) {
+
+ let total_support = calc_support()
+ let oppose_plus_bases = calc_oppose_bases()
+
+ if (total_support === i) list.push(ui.misc.total_support)
+ if (oppose_plus_bases === i) list.push(ui.misc.oppose_plus_bases)
+ if (view.misc.aid === i) list.push(ui.misc.aid)
+
+ if (view.govt.resources === i) list.push(ui.govt.resources)
+ if (view.auc.resources === i) list.push(ui.auc.resources)
+ if (view.cartels.resources === i) list.push(ui.cartels.resources)
+ if (view.farc.resources === i) list.push(ui.farc.resources)
+
+ if (i <= 30) y = 16
+ else if (i >= 77) y = 2486
+ else y = 16 + (i - 30) * 52.55
+
+ if (i < 1) x = 19 + 4
+ else if (i <= 30) x = 80 + (i - 1) * 52.07
+ else if (i <= 77) x = 1590
+ else x = 1590 - (i - 77) * 52.09
+
+ x = Math.round(x) + 24
+ y = Math.round(y) + 24
+
+ if (i < 1) layout_score_cell(list, x, y, 15, 25)
+ else if (i < 30) layout_score_cell(list, x, y, 0, 28)
+ else if (i === 30) layout_score_cell(list, x, y, -18, 25)
+ else if (i < 77) layout_score_cell(list, x, y, -41, 0)
+ else if (i === 77) layout_score_cell(list, x, y, -15, -19)
+ else layout_score_cell(list, x, y, 0, -19)
+ if (list.length > 0)
+ list.length = 0
+ }
+}
+
+function update_guerrillas_active(elts, guerrillas, active) {
+ for (let i = 0; i < guerrillas.length; ++i) {
+ if (active & (1 << i))
+ elts[i].classList.add("active")
+ else
+ elts[i].classList.remove("active")
+ }
+}
+
+function on_update() {
+ ui.header.classList.toggle("govt", view.active === "Government")
+ ui.header.classList.toggle("auc", view.active === "AUC")
+ ui.header.classList.toggle("cartels", view.active === "Cartels")
+ ui.header.classList.toggle("farc", view.active === "FARC")
+
+ switch (player) {
+ case "Government": favicon.href = "images/icon_govt.png"; break
+ case "AUC": favicon.href = "images/icon_auc.png"; break
+ case "Cartels": favicon.href = "images/icon_cartels.png"; break
+ case "FARC": favicon.href = "images/icon_farc.png"; break
+ }
+
+ ui.misc.president.style.left = [ 0, "254px", "337px", "420px" ][view.misc.president]
+
+ ui.player.govt.classList.toggle("active", view.active === "Government")
+ ui.player.auc.classList.toggle("active", view.active === "AUC")
+ ui.player.cartels.classList.toggle("active", view.active === "Cartels")
+ ui.player.farc.classList.toggle("active", view.active === "FARC")
+
+ if (view.propaganda > 0) {
+ ui.misc.propaganda.style.top = "744px"
+ ui.misc.propaganda.style.left = (1124 + 75 * (view.propaganda - 1)) + "px"
+ } else {
+ ui.misc.propaganda.style.top = "666px"
+ ui.misc.propaganda.style.left = "1029px"
+ }
+
+ ui.this_card.className = "card card_" + view.deck[0]
+ ui.next_card.className = "card card_" + view.deck[1]
+ ui.deck_size.textContent = view.deck[2]
+
+ layout_sop()
+ layout_score()
+
+ layout_cubes_available(view.govt.troops, ui.govt.troops, AVAILABLE, 114, 248)
+ layout_cubes_available(view.govt.police, ui.govt.police, AVAILABLE, 114, 448)
+ layout_guerrillas_available(view.auc.guerrillas, ui.auc.guerrillas, AVAILABLE, 196, 2370)
+ layout_guerrillas_available(view.cartels.guerrillas, ui.cartels.guerrillas, AVAILABLE, 1465, 1970)
+ layout_guerrillas_available(view.farc.guerrillas, ui.farc.guerrillas, AVAILABLE, 1396, 234)
+
+ let list = []
+ for (let s = 0; s < data.spaces.length; ++s) {
+ if (s <= last_pop) {
+ switch (view.misc.support[s]) {
+ case -2: ui.support[s].className = "token active_opposition"; break
+ case -1: ui.support[s].className = "token passive_opposition"; break
+ case 0: ui.support[s].className = "hide"; break
+ case 1: ui.support[s].className = "token passive_support"; break
+ case 2: ui.support[s].className = "token active_support"; break
+ }
+ }
+
+ if (s >= first_loc && s <= last_loc) {
+ if (set_has(view.misc.sabotage, s))
+ ui.sabotage[s].className = "token sabotage"
+ else
+ ui.sabotage[s].className = "hide"
+ }
+
+ if (s <= last_dept) {
+ if (set_has(view.misc.farc_zones, s))
+ ui.control[s].className = "token farc_zone"
+ else switch (view.misc.control[s]) {
+ case 0: ui.control[s].className = "hide"; break
+ case 1: ui.control[s].className = "token govt_control"; break
+ case 2: ui.control[s].className = "token farc_control"; break
+ }
+ }
+
+ update_guerrillas_active(ui.auc.guerrillas, view.auc.guerrillas, view.auc.active)
+ update_guerrillas_active(ui.cartels.guerrillas, view.cartels.guerrillas, view.cartels.active)
+ update_guerrillas_active(ui.farc.guerrillas, view.farc.guerrillas, view.farc.active)
+
+ list.length = 0
+ filter_piece_list(list, view.auc.guerrillas, ui.auc.guerrillas, s)
+ filter_piece_list(list, view.cartels.guerrillas, ui.cartels.guerrillas, s)
+ filter_piece_list(list, view.farc.guerrillas, ui.farc.guerrillas, s)
+ filter_piece_list(list, view.govt.troops, ui.govt.troops, s)
+ filter_piece_list(list, view.govt.police, ui.govt.police, s)
+
+ // TODO: associate shipments with other piece, not space
+ filter_piece_list(list, view.misc.shipments, ui.misc.shipments, s)
+
+ let xy = get_layout_xy(s)
+ if (xy)
+ layout_pieces(list, xy[0], xy[1])
+
+ list.length = 0
+ filter_piece_list(list, view.govt.bases, ui.govt.bases, s)
+ filter_piece_list(list, view.auc.bases, ui.auc.bases, s)
+ filter_piece_list(list, view.farc.bases, ui.farc.bases, s)
+ filter_piece_list(list, view.cartels.bases, ui.cartels.bases, s)
+
+ xy = get_center_xy(s)
+ if (xy)
+ layout_space_bases(list, xy[0], xy[1], s <= last_city ? get_layout_radius(s) : 0)
+ else
+ console.log("NO SPACE", s, data.spaces[s].name)
+
+ ui.spaces[s].classList.toggle("action", is_action("space", s))
+ }
+
+ list.length = 0
+ filter_piece_list(list, view.misc.shipments, ui.misc.shipments, AVAILABLE)
+ layout_available_bases(list, 1532, 1722, 2, 2, 89, 69)
+
+ list.length = 0
+ filter_piece_list(list, view.govt.bases, ui.govt.bases, AVAILABLE)
+ layout_available_bases(list, 287 + 177, 371, 3, 1, 61, 0)
+
+ list.length = 0
+ filter_piece_list(list, view.auc.bases, ui.auc.bases, AVAILABLE)
+ layout_available_bases(list, 446 + 360, 2386, 6, 1, 61, 0)
+
+ list.length = 0
+ filter_piece_list(list, view.farc.bases, ui.farc.bases, AVAILABLE)
+ layout_available_bases(list, 446 + 543, 2295, 9, 1, 61, 0)
+
+ list.length = 0
+ filter_piece_list(list, view.cartels.bases, ui.cartels.bases, AVAILABLE)
+ layout_available_bases(list, 1373 + 183, 2117, 3, 5, 63, 63)
+
+ action_button("train", "Train")
+ action_button("patrol", "Patrol")
+ action_button("sweep", "Sweep")
+ action_button("assault", "Assault")
+
+ action_button("rally", "Rally")
+ action_button("march", "March")
+ action_button("attack", "Attack")
+ action_button("terror", "Terror")
+
+ action_button("air_lift", "Air Lift")
+ action_button("air_strike", "Air Strike")
+ action_button("eradicate", "Eradicate")
+
+ action_button("extort", "Extort")
+ action_button("ambush", "Ambush")
+ action_button("kidnap", "Assassinate")
+ action_button("kidnap", "Kidnap")
+ action_button("kidnap", "Cultivate")
+ action_button("kidnap", "Process")
+ action_button("kidnap", "Bribe")
+
+ action_button("unshaded", "Unshaded")
+ action_button("shaded", "Shaded")
+
+ action_button("event", "Event")
+ action_button("limop", "LimOp")
+ action_button("undo", "Undo")
+}
+
+function on_focus_card_tip(c) {
+ document.getElementById("card_tip").className = "card card_" + c
+}
+
+function on_blur_card_tip(c) {
+ document.getElementById("card_tip").className = "hide"
+}
+
+function sub_card(match, p1) {
+ let x = p1 | 0
+ let n = data.cards[x].name
+ return `<span class="tip" onmouseenter="on_focus_card_tip(${x})" onmouseleave="on_blur_card_tip(${x})" onclick="on_click_card_tip(${x})">${n}</span>`
+}
+
+function sub_space(match, p1) {
+ let x = p1 | 0
+ let n = data.spaces[x].name
+ return `<span class="tip" onmouseenter="on_focus_space_tip(${x})" onmouseleave="on_blur_space_tip(${x})" onclick="on_click_space_tip(${x})">${n}</span>`
+}
+
+function on_log(text) {
+ let p = document.createElement("div")
+
+ if (text.match(/^>/)) {
+ text = text.substring(1)
+ p.className = "i"
+ }
+
+ text = text.replace(/&/g, "&amp;")
+ text = text.replace(/</g, "&lt;")
+ text = text.replace(/>/g, "&gt;")
+
+ text = text.replace(/C(\d+)/g, sub_card)
+ text = text.replace(/S(\d+)/g, sub_space)
+
+ if (text.match(/^\.h1/)) {
+ text = text.substring(4)
+ p.className = "h1"
+ }
+ else if (text.match(/^\.h2 Government/)) {
+ text = text.substring(3)
+ p.className = "h2 govt"
+ }
+ else if (text.match(/^\.h2 AUC/)) {
+ text = text.substring(3)
+ p.className = "h2 auc"
+ }
+ else if (text.match(/^\.h2 Cartels/)) {
+ text = text.substring(3)
+ p.className = "h2 cartels"
+ }
+ else if (text.match(/^\.h2 FARC/)) {
+ text = text.substring(3)
+ p.className = "h2 farc"
+ }
+ else if (text.match(/^\.h3/)) {
+ text = text.substring(4)
+ p.className = "h3"
+ }
+ else if (text.match(/^\.h4/)) {
+ text = text.substring(4)
+ p.className = "h4"
+ }
+
+ p.innerHTML = text
+ return p
+}
+
+function set_has(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else
+ return true
+ }
+ return false
+}
+
+init_ui()
+scroll_with_middle_mouse("main")
diff --git a/rules.js b/rules.js
new file mode 100644
index 0000000..717b521
--- /dev/null
+++ b/rules.js
@@ -0,0 +1,1183 @@
+"use strict"
+
+let states = {}
+let game = null
+let view = null
+
+const data = require("./data.js")
+
+const GOVT = "Government"
+const AUC = "AUC"
+const CARTELS = "Cartels"
+const FARC = "FARC"
+
+// For 3 and 2 player games
+const AUC_CARTELS = "AUC + Cartels"
+const GOVT_AUC = "Government + AUC"
+const FARC_CARTELS = "FARC + Cartels"
+
+// Sequence of Play options
+const ELIGIBLE = 0
+const SOP_1ST_OP_ONLY = 1
+const SOP_2ND_LIMOP = 2
+const SOP_1ST_OP_AND_SA = 3
+const SOP_2ND_LIMOP_OR_EVENT = 4
+const SOP_1ST_EVENT = 5
+const SOP_2ND_OP_AND_SA = 6
+const SOP_PASS = 7
+const INELIGIBLE = 8
+
+// Support
+const ACTIVE_SUPPORT = 2
+const PASSIVE_SUPPORT = 1
+const NEUTRAL = 0
+const PASSIVE_OPPOSITION = -1
+const ACTIVE_OPPOSITION = -2
+
+// Control ?
+const UNCONTROLLED = 0
+const CTL_GOVERNMENT = 1
+const CTL_AUC = 2
+const CTL_CARTELS = 3
+const CTL_FARC = 4
+
+const SAMPER = 1
+const PASTRANA = 2
+const URIBE = 3
+
+const first_pop = 1
+const first_city = 0
+const last_city = 10
+const first_dept = 11
+const last_pop = 22
+const last_dept = 26
+const first_foreign = 27
+const last_foreign = 31
+const first_loc = 32
+const last_loc = 49
+
+const AVAILABLE = -1
+
+// Cities
+const BOGOTA = 0
+const CALI = 1
+const MEDELLIN = 2
+const BUCARAMANGA = 3
+const IBAGUE = 4
+const SANTA_MARTA = 5
+const CARTAGENA = 6
+const CUCUTA = 7
+const NEIVA = 8
+const PASTO = 9
+const SINCELEJO = 10
+
+// 1+ Pop Depts
+const ATLANTICO = 11
+const CHOCO = 12
+const NARINO = 13
+const META_WEST = 14
+const GUAVIARE = 15
+const PUTUMAYO = 16
+const CESAR = 17
+const ANTIOQUIA = 18
+const SANTANDER = 19
+const HUILA = 20
+const ARAUCA = 21
+const META_EAST = 22
+
+// 0 Pop Depts
+const VICHADA = 23
+const GUAINIA = 24
+const VAUPES = 25
+const AMAZONAS = 26
+
+// Foreign Countries
+const BRASIL = 27
+const ECUADOR = 28
+const PANAMA = 29
+const PERU = 30
+const VENEZUELA = 31
+
+const COASTAL_SPACES = [ ATLANTICO, CHOCO, NARINO, CESAR ]
+
+exports.roles = function (scenario) {
+ if (scenario.startsWith("2P"))
+ return [ GOVT_AUC, FARC_CARTELS ]
+ if (scenario.startsWith("3P"))
+ return [ GOVT, FARC, AUC_CARTELS ]
+ return [ GOVT, AUC, CARTELS, FARC ]
+}
+
+exports.scenarios = [
+ "Standard",
+ "Short",
+ "Quick",
+ "3P Standard",
+ "3P Short",
+ "3P Quick",
+ "2P Standard",
+ "2P Short",
+ "2P Quick",
+]
+
+function load_game(state) {
+ game = state
+ if (game.scenario !== 4)
+ game.active = game.save_active
+}
+
+function save_game() {
+ if (game.scenario === 3) {
+ game.save_active = game.active
+ if (game.active === AUC || game.active === CARTELS)
+ game.active = AUC_CARTELS
+ }
+ if (game.scenario === 2) {
+ game.save_active = game.active
+ if (game.active === GOVT || game.active === AUC)
+ game.active = GOVT_AUC
+ if (game.active === FARC || game.active === CARTELS)
+ game.active = FARC_CARTELS
+ }
+ return game
+}
+
+exports.setup = function (seed, scenario, options) {
+ game = {
+ seed,
+ log: [],
+ undo: [],
+
+ scenario: 4,
+ active: null,
+ state: null,
+
+ op_spaces: null,
+ sa_spaces: null,
+
+ deck: [],
+ misc: {
+ aid: 0,
+ president: 0,
+ shipments: [ AVAILABLE, AVAILABLE, AVAILABLE, AVAILABLE ],
+ control: Array(27).fill(0),
+ support: Array(23).fill(NEUTRAL),
+ farc_zones: [],
+ terror: [],
+ sabotage: [],
+ },
+ govt: {
+ cylinder: ELIGIBLE,
+ resources: 0,
+ troops: Array(30).fill(AVAILABLE),
+ police: Array(30).fill(AVAILABLE),
+ bases: Array(3).fill(AVAILABLE),
+ },
+ auc: {
+ cylinder: ELIGIBLE,
+ resources: 0,
+ guerrillas: Array(18).fill(AVAILABLE),
+ bases: Array(6).fill(AVAILABLE),
+ active: 0,
+ },
+ cartels: {
+ cylinder: ELIGIBLE,
+ resources: 0,
+ guerrillas: Array(12).fill(AVAILABLE),
+ bases: Array(15).fill(AVAILABLE),
+ active: 0,
+ },
+ farc: {
+ cylinder: ELIGIBLE,
+ resources: 0,
+ guerrillas: Array(30).fill(AVAILABLE),
+ bases: Array(9).fill(AVAILABLE),
+ active: 0,
+ },
+ }
+
+ if (scenario.startsWith("3P"))
+ game.scenario = 3
+ if (scenario.startsWith("2P"))
+ game.scenario = 2
+
+ setup_standard()
+
+ if (scenario === "Quick") {
+ log_h1("Scenario: Quick")
+ setup_quick()
+ setup_quick_deck()
+ } else if (scenario === "Short") {
+ setup_short_deck()
+ } else {
+ setup_standard_deck()
+ }
+
+ update_control()
+
+ goto_card()
+
+ return save_game()
+}
+
+function setup_standard() {
+ game.misc.aid = 9
+ game.misc.president = SAMPER
+ game.govt.resources = 40
+ game.auc.resources = 10
+ game.cartels.resources = 10
+ game.farc.resources = 10
+
+ set_support(ATLANTICO, ACTIVE_SUPPORT)
+ set_support(SANTANDER, ACTIVE_SUPPORT)
+ for (let s = first_city; s <= last_city; ++s)
+ if (s !== CALI)
+ set_support(s, ACTIVE_SUPPORT)
+
+ set_support(CHOCO, ACTIVE_OPPOSITION)
+ set_support(ARAUCA, ACTIVE_OPPOSITION)
+ set_support(META_EAST, ACTIVE_OPPOSITION)
+ set_support(META_WEST, ACTIVE_OPPOSITION)
+ set_support(GUAVIARE, ACTIVE_OPPOSITION)
+ set_support(PUTUMAYO, ACTIVE_OPPOSITION)
+ set_support(NARINO, ACTIVE_OPPOSITION)
+
+ place_piece(game.govt.troops, 3, BOGOTA)
+ place_piece(game.govt.troops, 3, MEDELLIN)
+ place_piece(game.govt.troops, 3, CALI)
+ place_piece(game.govt.troops, 3, SANTANDER)
+ place_piece(game.govt.police, 2, BOGOTA)
+ for (let s = first_city; s <= last_city; ++s)
+ if (s !== BOGOTA)
+ place_piece(game.govt.police, 1, s)
+ place_piece(game.govt.bases, 1, SANTANDER)
+
+ place_piece(game.farc.guerrillas, 1, NARINO)
+ place_piece(game.farc.guerrillas, 1, CHOCO)
+ place_piece(game.farc.guerrillas, 1, SANTANDER)
+ place_piece(game.farc.guerrillas, 1, HUILA)
+ place_piece(game.farc.guerrillas, 1, ARAUCA)
+ place_piece(game.farc.guerrillas, 1, META_EAST)
+ place_piece(game.farc.guerrillas, 2, META_WEST)
+ place_piece(game.farc.guerrillas, 2, GUAVIARE)
+ place_piece(game.farc.guerrillas, 2, PUTUMAYO)
+ place_piece(game.farc.bases, 1, CHOCO)
+ place_piece(game.farc.bases, 1, HUILA)
+ place_piece(game.farc.bases, 1, ARAUCA)
+ place_piece(game.farc.bases, 1, META_EAST)
+ place_piece(game.farc.bases, 1, META_WEST)
+ place_piece(game.farc.bases, 1, GUAVIARE)
+
+ place_piece(game.auc.guerrillas, 1, ATLANTICO)
+ place_piece(game.auc.guerrillas, 1, ANTIOQUIA)
+ place_piece(game.auc.guerrillas, 1, SANTANDER)
+ place_piece(game.auc.guerrillas, 1, ARAUCA)
+ place_piece(game.auc.guerrillas, 1, GUAVIARE)
+ place_piece(game.auc.guerrillas, 1, PUTUMAYO)
+ place_piece(game.auc.bases, 1, ANTIOQUIA)
+
+ place_piece(game.cartels.guerrillas, 1, CALI)
+ place_piece(game.cartels.guerrillas, 1, PUTUMAYO)
+ place_piece(game.cartels.bases, 1, CALI)
+ place_piece(game.cartels.bases, 1, META_EAST)
+ place_piece(game.cartels.bases, 1, META_WEST)
+ place_piece(game.cartels.bases, 1, GUAVIARE)
+ place_piece(game.cartels.bases, 2, PUTUMAYO)
+}
+
+function setup_quick() {
+ place_piece(game.cartels.guerrillas, 4, MEDELLIN)
+ place_piece(game.cartels.bases, 1, MEDELLIN)
+
+ set_support(CALI, ACTIVE_SUPPORT)
+ place_piece(game.govt.police, 4, CALI)
+ remove_piece(game.cartels.guerrillas, 1, CALI)
+ remove_piece(game.cartels.bases, 1, CALI)
+
+ place_piece(game.govt.troops, 6, BOGOTA)
+
+ place_piece(game.auc.bases, 1, SANTANDER)
+
+ set_support(ARAUCA, NEUTRAL)
+ place_piece(game.auc.guerrillas, 1, ARAUCA)
+
+ set_add(game.misc.farc_zones, META_WEST)
+ place_piece(game.farc.guerrillas, 4, META_WEST)
+
+ set_support(HUILA, ACTIVE_OPPOSITION)
+ place_piece(game.farc.guerrillas, 3, HUILA)
+ place_piece(game.auc.guerrillas, 2, HUILA)
+ place_piece(game.cartels.bases, 1, HUILA)
+
+ place_piece(game.farc.guerrillas, 2, VAUPES)
+
+ game.auc.resources = 5
+ game.farc.resources = 10
+ game.cartels.resources = 20
+ game.govt.resources = 30
+
+ game.misc.president = PASTRANA
+}
+
+function shuffle_all_cards() {
+ let deck = []
+ for (let i = 1; i <= 72; ++i)
+ deck.push(i)
+ shuffle(deck)
+ return deck
+}
+
+function setup_standard_deck() {
+ let cards = shuffle_all_cards()
+ let piles = [
+ cards.slice(0, 15),
+ cards.slice(15, 15+15),
+ cards.slice(30, 30+15),
+ cards.slice(45, 45+15),
+ ]
+ piles[0].push(73)
+ piles[1].push(74)
+ piles[2].push(75)
+ piles[3].push(76)
+ shuffle(piles[0])
+ shuffle(piles[1])
+ shuffle(piles[2])
+ shuffle(piles[3])
+ game.deck = piles[0].concat(piles[1], piles[2], piles[3])
+}
+
+function setup_short_deck() {
+ let cards = shuffle_all_cards()
+ let piles = [
+ cards.slice(0, 15),
+ cards.slice(15, 15+15),
+ cards.slice(30, 30+15),
+ ]
+ piles[0].push(73)
+ piles[1].push(74)
+ piles[2].push(75)
+ shuffle(piles[0])
+ shuffle(piles[1])
+ shuffle(piles[2])
+ game.deck = piles[0].concat(piles[1], piles[2], piles[3])
+}
+
+function setup_quick_deck() {
+ let cards = shuffle_all_cards()
+ let piles = [
+ cards.slice(0, 6),
+ cards.slice(6, 6+6),
+ cards.slice(12, 12+6),
+ cards.slice(24, 24+6),
+ ]
+ piles[1].push(73)
+ piles[3].push(74)
+ shuffle(piles[1])
+ shuffle(piles[3])
+ game.deck = piles[0].concat(piles[1], piles[2], piles[3])
+}
+
+function set_support(place, amount) {
+ game.misc.support[place] = amount
+}
+
+function get_support(place, amount) {
+ return game.misc.support[place]
+}
+
+function place_piece(list, count, where) {
+ for (let i = 0; i < list.length && count > 0; ++i) {
+ if (list[i] < 0) {
+ list[i] = where
+ --count
+ }
+ }
+ if (count !== 0)
+ throw Error("bad piece count")
+}
+
+function remove_piece(list, count, where) {
+ for (let i = 0; i < list.length && count > 0; ++i) {
+ if (list[i] === where) {
+ list[i] = AVAILABLE
+ --count
+ }
+ }
+ if (count !== 0)
+ throw Error("bad piece count")
+}
+
+function count_pieces_imp(s, list) {
+ let n = 0
+ for (let i = 0; i < list.length; ++i)
+ if (list[i] === s)
+ ++n
+ return n
+}
+
+function update_control() {
+ for (let s = 0; s <= last_dept; ++s) {
+ let g = count_pieces_imp(s, game.govt.troops) +
+ count_pieces_imp(s, game.govt.police) +
+ count_pieces_imp(s, game.govt.bases)
+ let a = count_pieces_imp(s, game.auc.guerrillas) +
+ count_pieces_imp(s, game.auc.bases)
+ let c = count_pieces_imp(s, game.cartels.guerrillas) +
+ count_pieces_imp(s, game.cartels.bases)
+ let f = count_pieces_imp(s, game.farc.guerrillas) +
+ count_pieces_imp(s, game.farc.bases)
+ if (g > a + c + f)
+ game.misc.control[s] = 1
+ else if (f > g + a + c)
+ game.misc.control[s] = 2
+ else
+ game.misc.control[s] = 0
+ }
+}
+
+// === SEQUENCE OF PLAY ===
+
+function this_card() {
+ return game.deck[0]
+}
+
+function goto_card() {
+ if (this_card() > 72)
+ goto_propaganda_card()
+ else
+ goto_event_card()
+}
+
+function adjust_eligibility(faction) {
+ if (faction.cylinder === INELIGIBLE || faction.cylinder === SOP_PASS)
+ faction.cylinder = ELIGIBLE
+ else if (faction.cylinder !== ELIGIBLE)
+ faction.cylinder = INELIGIBLE
+}
+
+function end_card() {
+ adjust_eligibility(game.govt)
+ adjust_eligibility(game.auc)
+ adjust_eligibility(game.cartels)
+ adjust_eligibility(game.farc)
+
+ clear_undo()
+ array_remove(game.deck, 0)
+ goto_card()
+}
+
+function current_faction() {
+ switch (game.active) {
+ case GOVT: return game.govt
+ case AUC: return game.auc
+ case CARTELS: return game.cartels
+ case FARC: return game.farc
+ }
+}
+
+function is_eligible(faction) {
+ switch (faction) {
+ case GOVT: return game.govt.cylinder === ELIGIBLE
+ case AUC: return game.auc.cylinder === ELIGIBLE
+ case CARTELS: return game.cartels.cylinder === ELIGIBLE
+ case FARC: return game.farc.cylinder === ELIGIBLE
+ }
+ return false
+}
+
+function next_eligible_faction() {
+ let order = data.cards[this_card()].order
+ for (let faction of order)
+ if (is_eligible(faction))
+ return faction
+ return null
+}
+
+function did_option(e) {
+ return (
+ game.govt.cylinder === e ||
+ game.auc.cylinder === e ||
+ game.cartels.cylinder === e ||
+ game.farc.cylinder === e
+ )
+}
+
+function goto_event_card() {
+ log_h1("C" + this_card())
+ resume_event_card()
+}
+
+function resume_event_card() {
+ clear_undo()
+ let did_1st = (did_option(SOP_1ST_OP_ONLY) || did_option(SOP_1ST_OP_AND_SA) || did_option(SOP_1ST_EVENT))
+ let did_2nd = (did_option(SOP_2ND_LIMOP) || did_option(SOP_2ND_LIMOP_OR_EVENT) || did_option(SOP_2ND_OP_AND_SA))
+ if (did_1st) {
+ if (did_2nd)
+ end_card()
+ else
+ goto_eligible2()
+ } else {
+ goto_eligible1()
+ }
+}
+
+function goto_eligible1() {
+ game.active = next_eligible_faction()
+ if (game.active === null)
+ end_card()
+ else
+ game.state = "eligible1"
+}
+
+function goto_eligible2() {
+ game.active = next_eligible_faction()
+ if (game.active === null)
+ end_card()
+ else
+ game.state = "eligible2"
+}
+
+states.eligible1 = {
+ inactive: "1st Eligible",
+ prompt() {
+ view.prompt = "1st Eligible: Choose a Sequence of Play option."
+ gen_action("sop", SOP_1ST_OP_ONLY)
+ gen_action("sop", SOP_1ST_OP_AND_SA)
+ gen_action("sop", SOP_1ST_EVENT)
+ gen_action("sop", SOP_PASS)
+ },
+ sop(e) {
+ push_undo()
+ let faction = current_faction()
+ faction.cylinder = e
+ switch (e) {
+ case SOP_PASS:
+ goto_pass()
+ break
+ case SOP_1ST_OP_ONLY:
+ goto_op_only()
+ break
+ case SOP_1ST_OP_AND_SA:
+ goto_op_and_sa()
+ break
+ case SOP_1ST_EVENT:
+ goto_event()
+ break
+ }
+ },
+}
+
+states.eligible2 = {
+ inactive: "2nd Eligible",
+ prompt() {
+ view.prompt = "2nd Eligible: Choose a Sequence of Play option."
+ if (did_option(SOP_1ST_OP_ONLY))
+ gen_action("sop", SOP_2ND_LIMOP)
+ if (did_option(SOP_1ST_OP_AND_SA))
+ gen_action("sop", SOP_2ND_LIMOP_OR_EVENT)
+ if (did_option(SOP_1ST_EVENT))
+ gen_action("sop", SOP_2ND_OP_AND_SA)
+ gen_action("sop", SOP_PASS)
+ },
+ sop(e) {
+ push_undo()
+ let faction = current_faction()
+ faction.cylinder = e
+ switch (e) {
+ case SOP_PASS:
+ goto_pass()
+ break
+ case SOP_2ND_LIMOP:
+ goto_op_only()
+ break
+ case SOP_2ND_LIMOP_OR_EVENT:
+ goto_limop_or_event()
+ break
+ case SOP_2ND_OP_AND_SA:
+ goto_op_and_sa()
+ break
+ }
+ },
+}
+
+function goto_pass() {
+ log_h2(game.active + " - Pass")
+ let faction = current_faction()
+ if (game.active === GOVT)
+ faction.resources += 3
+ else
+ faction.resources += 1
+ resume_event_card()
+}
+
+function goto_limop_or_event() {
+ push_undo()
+ game.state = "limop_or_event"
+}
+
+states.limop_or_event = {
+ prompt() {
+ view.prompt = "2nd Eligible: Event or Limited Operation?"
+ view.actions.event = 1
+ view.actions.limop = 1
+ },
+ event() {
+ goto_event()
+ },
+ limop() {
+ goto_limop()
+ }
+}
+
+function goto_event() {
+ log_h2(game.active + " - Event")
+ log("TODO: Event")
+ resume_event_card()
+}
+
+function goto_op_only() {
+ log_h2(game.active + " - Op only")
+ game.state = "op"
+ game.op_spaces = []
+ game.sa_spaces = null
+}
+
+function goto_op_and_sa() {
+ log_h2(game.active + " - Op + Special")
+ game.state = "op"
+ game.op_spaces = []
+ game.sa_spaces = []
+}
+
+function goto_limop() {
+ log_h2(game.active + " - LimOp")
+ game.state = "op"
+ game.op_spaces = []
+ game.sa_spaces = null
+}
+
+function can_use_special_activity() {
+ let faction = current_faction()
+ if (faction.cylinder === SOP_1ST_OP_AND_SA || faction.cylinder === SOP_2ND_OP_AND_SA)
+ return true
+ return false
+}
+
+states.op = {
+ prompt() {
+ view.prompt = "Choose an Operation."
+ if (game.active === GOVT) {
+ view.actions.train = 1
+ view.actions.patrol = 1
+ view.actions.sweep = 1
+ view.actions.assault = 1
+ } else {
+ view.actions.rally = 1
+ view.actions.march = 1
+ view.actions.attack = 1
+ view.actions.terror = 1
+ }
+ },
+
+ train() {
+ push_undo()
+ log_h3("Train")
+ game.state = "train"
+ },
+ patrol() {
+ push_undo()
+ log_h3("Patrol")
+ game.state = "patrol"
+ },
+ sweep() {
+ push_undo()
+ log_h3("Sweep")
+ game.state = "sweep"
+ },
+ assault() {
+ push_undo()
+ log_h3("Assault")
+ game.state = "assault"
+ },
+
+ rally() {
+ push_undo()
+ log_h3("Rally")
+ game.state = "rally"
+ },
+ march() {
+ push_undo()
+ log_h3("March")
+ game.state = "march"
+ },
+ attack() {
+ push_undo()
+ log_h3("Attack")
+ game.state = "attack"
+ },
+ terror() {
+ push_undo()
+ log_h3("Terror")
+ game.state = "terror"
+ },
+}
+
+states.train = {
+ prompt() {
+ let faction = current_faction()
+
+ view.prompt = "Train: Select spaces."
+
+ if (can_use_special_activity()) {
+ view.actions.air_lift = 1
+ view.actions.eradicate = 1
+ }
+
+ // Any Departments or Cities
+ if (faction.resources >= 3) {
+ for (let s = 0; s <= last_dept; ++s) {
+ if (!set_has(game.op_spaces, s))
+ gen_action("space", s)
+ }
+ }
+ },
+ air_lift() {
+ push_undo()
+ game.state = "air_lift"
+ },
+ eradicate() {
+ push_undo()
+ game.state = "eradicate"
+ },
+ space(s) {
+ push_undo()
+ logi(`S${s}.`)
+ let faction = current_faction()
+ faction.resources -= 3
+ set_add(game.op_spaces, s)
+ },
+}
+
+// === GAME OVER ===
+
+function goto_game_over(result, victory) {
+ game = { ...game } // make a copy so we can add properties!
+ game.state = "game_over"
+ game.active = "None"
+ game.result = result
+ game.victory = victory
+ log_h1("Game Over")
+ log(game.victory)
+ return true
+}
+
+states.game_over = {
+ get inactive() {
+ return game.victory
+ },
+ prompt() {
+ view.prompt = game.victory
+ },
+}
+
+// === UNCOMMON TEMPLATE ===
+
+function log_br() {
+ if (game.log.length > 0 && game.log[game.log.length - 1] !== "")
+ game.log.push("")
+}
+
+function log(msg) {
+ game.log.push(msg)
+}
+
+function logi(msg) {
+ game.log.push(">" + msg)
+}
+
+function log_h1(msg) {
+ log_br()
+ log(".h1 " + msg)
+ log_br()
+}
+
+function log_h2(msg) {
+ log_br()
+ log(".h2 " + msg)
+ log_br()
+}
+
+function log_h3(msg) {
+ log_br()
+ log(".h3 " + msg)
+}
+
+function log_h4(msg) {
+ log_br()
+ log(".h4 " + msg)
+}
+
+function gen_action(action, argument) {
+ if (!(action in view.actions))
+ view.actions[action] = []
+ set_add(view.actions[action], argument)
+}
+
+function is_current_active(current) {
+ switch (current) {
+ case GOVT_AUC:
+ return game.active === GOVT || game.active === AUC
+ case FARC_CARTELS:
+ return game.active === FARC || game.active === CARTELS
+ case AUC_CARTELS:
+ return game.active === AUC || game.active === CARTELS
+ default:
+ return game.active === current
+ }
+}
+
+exports.view = function (state, current) {
+ load_game(state)
+
+ let this_card = game.deck[0]
+ let next_card = game.deck[1]
+ let deck_size = Math.max(0, game.deck.length - 2)
+
+ view = {
+ active: game.active,
+ prompt: null,
+ actions: null,
+ log: game.log,
+
+ deck: [ this_card, next_card, deck_size ],
+ op_spaces: game.op_spaces,
+ sa_spaces: game.sa_spaces,
+ misc: game.misc,
+ govt: game.govt,
+ auc: game.auc,
+ cartels: game.cartels,
+ farc: game.farc,
+ }
+
+ if (game.state === "game_over") {
+ view.prompt = game.victory
+ } else if (current === "Observer" || !is_current_active(current)) {
+ let inactive = states[game.state].inactive || game.state
+ view.prompt = `Waiting for ${game.active} \u2014 ${inactive}.`
+ } else {
+ view.actions = {}
+ view.who = game.who
+ if (states[game.state])
+ states[game.state].prompt(current)
+ else
+ view.prompt = "Unknown state: " + game.state
+ if (view.actions.undo === undefined) {
+ if (game.undo && game.undo.length > 0)
+ view.actions.undo = 1
+ else
+ view.actions.undo = 0
+ }
+ }
+
+ save_game()
+ return view
+}
+
+exports.action = function (state, current, action, arg) {
+ load_game(state)
+ Object.seal(game) // XXX: don't allow adding properties
+ let S = states[game.state]
+ if (S && action in S) {
+ S[action](arg, current)
+ } else {
+ if (action === "undo" && game.undo && game.undo.length > 0)
+ pop_undo()
+ else
+ throw new Error("Invalid action: " + action)
+ }
+ return save_game()
+}
+
+exports.is_checkpoint = function (a, b) {
+ return a.turn !== b.turn
+}
+
+// === COMMON LIBRARY ===
+
+// Packed array of small numbers in one word
+
+function pack1_get(word, n) {
+ return (word >>> n) & 1
+}
+
+function pack2_get(word, n) {
+ n = n << 1
+ return (word >>> n) & 3
+}
+
+function pack4_get(word, n) {
+ n = n << 2
+ return (word >>> n) & 15
+}
+
+function pack1_set(word, n, x) {
+ return (word & ~(1 << n)) | (x << n)
+}
+
+function pack2_set(word, n, x) {
+ n = n << 1
+ return (word & ~(3 << n)) | (x << n)
+}
+
+function pack4_set(word, n, x) {
+ n = n << 2
+ return (word & ~(15 << n)) | (x << n)
+}
+
+function clear_undo() {
+ if (game.undo.length > 0)
+ game.undo = []
+}
+
+function push_undo() {
+ let copy = {}
+ for (let k in game) {
+ let v = game[k]
+ if (k === "undo")
+ continue
+ else if (k === "log")
+ v = v.length
+ else if (typeof v === "object" && v !== null)
+ v = object_copy(v)
+ copy[k] = v
+ }
+ game.undo.push(copy)
+}
+
+function pop_undo() {
+ let save_log = game.log
+ let save_undo = game.undo
+ game = save_undo.pop()
+ save_log.length = game.log
+ game.log = save_log
+ game.undo = save_undo
+}
+
+function random(range) {
+ // An MLCG using integer arithmetic with doubles.
+ // https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/S0025-5718-99-00996-5.pdf
+ // m = 2**35 − 31
+ return (game.seed = (game.seed * 200105) % 34359738337) % range
+}
+
+function shuffle(list) {
+ // Fisher-Yates shuffle
+ for (let i = list.length - 1; i > 0; --i) {
+ let j = random(i + 1)
+ let tmp = list[j]
+ list[j] = list[i]
+ list[i] = tmp
+ }
+}
+
+// Fast deep copy for objects without cycles
+function object_copy(original) {
+ if (Array.isArray(original)) {
+ let n = original.length
+ let copy = new Array(n)
+ for (let i = 0; i < n; ++i) {
+ let v = original[i]
+ if (typeof v === "object" && v !== null)
+ copy[i] = object_copy(v)
+ else
+ copy[i] = v
+ }
+ return copy
+ } else {
+ let copy = {}
+ for (let i in original) {
+ let v = original[i]
+ if (typeof v === "object" && v !== null)
+ copy[i] = object_copy(v)
+ else
+ copy[i] = v
+ }
+ return copy
+ }
+}
+
+// Array remove and insert (faster than splice)
+
+function array_remove_item(array, item) {
+ let n = array.length
+ for (let i = 0; i < n; ++i)
+ if (array[i] === item)
+ return array_remove(array, i)
+}
+
+function array_remove(array, index) {
+ let n = array.length
+ for (let i = index + 1; i < n; ++i)
+ array[i - 1] = array[i]
+ array.length = n - 1
+}
+
+function array_insert(array, index, item) {
+ for (let i = array.length; i > index; --i)
+ array[i] = array[i - 1]
+ array[index] = item
+}
+
+function array_remove_pair(array, index) {
+ let n = array.length
+ for (let i = index + 2; i < n; ++i)
+ array[i - 2] = array[i]
+ array.length = n - 2
+}
+
+function array_insert_pair(array, index, key, value) {
+ for (let i = array.length; i > index; i -= 2) {
+ array[i] = array[i - 2]
+ array[i + 1] = array[i - 1]
+ }
+ array[index] = key
+ array[index + 1] = value
+}
+
+// Set as plain sorted array
+
+function set_has(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else
+ return true
+ }
+ return false
+}
+
+function set_add(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else
+ return
+ }
+ array_insert(set, a, item)
+}
+
+function set_delete(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else {
+ array_remove(set, m)
+ return
+ }
+ }
+}
+
+function set_toggle(set, item) {
+ let a = 0
+ let b = set.length - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = set[m]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else {
+ array_remove(set, m)
+ return
+ }
+ }
+ array_insert(set, a, item)
+}
+
+// Map as plain sorted array of key/value pairs
+
+function map_has(map, key) {
+ let a = 0
+ let b = (map.length >> 1) - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = map[m << 1]
+ if (key < x)
+ b = m - 1
+ else if (key > x)
+ a = m + 1
+ else
+ return true
+ }
+ return false
+}
+
+function map_get(map, key, missing) {
+ let a = 0
+ let b = (map.length >> 1) - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = map[m << 1]
+ if (key < x)
+ b = m - 1
+ else if (key > x)
+ a = m + 1
+ else
+ return map[(m << 1) + 1]
+ }
+ return missing
+}
+
+function map_set(map, key, value) {
+ let a = 0
+ let b = (map.length >> 1) - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = map[m << 1]
+ if (key < x)
+ b = m - 1
+ else if (key > x)
+ a = m + 1
+ else {
+ map[(m << 1) + 1] = value
+ return
+ }
+ }
+ array_insert_pair(map, a << 1, key, value)
+}
+
+function map_delete(map, item) {
+ let a = 0
+ let b = (map.length >> 1) - 1
+ while (a <= b) {
+ let m = (a + b) >> 1
+ let x = map[m << 1]
+ if (item < x)
+ b = m - 1
+ else if (item > x)
+ a = m + 1
+ else {
+ array_remove_pair(map, m << 1)
+ return
+ }
+ }
+}
diff --git a/tokens.1x/shipment.png b/tokens.1x/shipment.png
new file mode 100644
index 0000000..52c9d90
--- /dev/null
+++ b/tokens.1x/shipment.png
Binary files differ
diff --git a/tokens.2x/shipment.png b/tokens.2x/shipment.png
new file mode 100644
index 0000000..dce5b6b
--- /dev/null
+++ b/tokens.2x/shipment.png
Binary files differ
diff --git a/tools/gendata.js b/tools/gendata.js
index 58340af..5d59674 100644
--- a/tools/gendata.js
+++ b/tools/gendata.js
@@ -1,120 +1,122 @@
+let fs = require("fs")
+
let data = {}
+function to_ascii(s) {
+ return s.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
+}
+
const IMAP = { G: "Government", F: "FARC", A: "AUC", C: "Cartels" }
-let initiative = null
+let order = null
let cards = [ null ]
-function def_initiative(s) {
- initiative = [ IMAP[s[0]], IMAP[s[1]], IMAP[s[2]], IMAP[s[3]] ]
+function def_order(s) {
+ order = [ IMAP[s[0]], IMAP[s[1]], IMAP[s[2]], IMAP[s[3]] ]
}
function def_card(number, name) {
- cards[number] = { number, name, initiative }
+ cards[number] = { number, name, order }
}
-def_initiative("GFAC")
+def_order("GFAC")
def_card(1, "1st Division")
def_card(2, "Ospina & Mora")
def_card(3, "Tapias")
-def_initiative("GFCA")
+def_order("GFCA")
def_card(4, "Caño Limón - Coveñas")
def_card(5, "Occidental & Ecopetrol")
def_card(6, "Oil Spill")
-def_initiative("GAFC")
+def_order("GAFC")
def_card(7, "7th Special Forces")
def_card(8, "Fuerza Aérea Colombiana")
def_card(9, "High Mountain Battalions")
-def_initiative("GACF")
+def_order("GACF")
def_card(10, "Blackhawks")
def_card(11, "National Defense & Security Council")
def_card(12, "Plan Colombia")
-def_initiative("GCFA")
+def_order("GCFA")
def_card(13, "Plan Meteoro")
def_card(14, "Tres Esquinas")
def_card(15, "War Tax")
-def_initiative("GCAF")
+def_order("GCAF")
def_card(16, "Coffee Prices")
def_card(17, "Madrid Donors")
def_card(18, "NSPD-18")
-def_initiative("FGAC")
+def_order("FGAC")
def_card(19, "General Offensive")
def_card(20, "Mono Jojoy")
def_card(21, "Raúl Reyes")
-def_initiative("FGCA")
+def_order("FGCA")
def_card(22, "Alfonso Cano")
def_card(23, "DoD Contractors")
def_card(24, "Operación Jaque")
-def_initiative("FAGC")
+def_order("FAGC")
def_card(25, "Ejército de Liberación Nacional")
def_card(26, "Gramaje")
def_card(27, "Misil Antiaéreo")
-def_initiative("FACG")
+def_order("FACG")
def_card(28, "Hugo Chávez")
def_card(29, "Kill Zone")
def_card(30, "Peace Commission")
-def_initiative("FCGA")
+def_order("FCGA")
def_card(31, "Betancourt")
def_card(32, "Secuestrados")
def_card(33, "Sucumbíos")
-def_initiative("FCAG")
+def_order("FCAG")
def_card(34, "Airdropped AKs")
def_card(35, "Crop Substitution")
def_card(36, "Zona de Convivencia")
-def_initiative("AGFC")
+def_order("AGFC")
def_card(37, "Former Military")
def_card(38, "National Coordination Center")
-def_card(39, "Soldados campesinos")
-def_initiative("AGCF")
+def_card(39, "Soldados Campesinos")
+def_order("AGCF")
def_card(40, "Demobilization")
def_card(41, "Mancuso")
def_card(42, "Senado & Cámara")
-def_initiative("AFGC")
+def_order("AFGC")
def_card(43, "Calima Front")
def_card(44, "Colombia Nueva")
def_card(45, "Los Derechos Humanos")
-def_initiative("AFCG")
+def_order("AFCG")
def_card(46, "Limpieza")
def_card(47, "Pinto & del Rosario")
def_card(48, "Unión Sindical Obrera")
-def_initiative("ACGF")
+def_order("ACGF")
def_card(49, "Bloques")
def_card(50, "Carabineros")
def_card(51, "Pipeline Repairs")
-def_initiative("ACFG")
+def_order("ACFG")
def_card(52, "Castaño")
def_card(53, "Criminal Air Force")
def_card(54, "Deserters & Defectors")
-def_initiative("CGFA")
+def_order("CGFA")
def_card(55, "DEA Agents")
def_card(56, "Drogas La Rebaja")
def_card(57, "Op Millennium")
-def_initiative("CGAF")
+def_order("CGAF")
def_card(58, "General Serrano")
def_card(59, "Salcedo")
def_card(60, "The Chess Player")
-def_initiative("CFGA")
+def_order("CFGA")
def_card(61, "Air Bridge")
def_card(62, "Amazonía")
def_card(63, "Narco-War")
-def_initiative("CFAG")
+def_order("CFAG")
def_card(64, "Cocaine Labs")
def_card(65, "Poppies")
def_card(66, "Tingo María")
-def_initiative("CAGF")
+def_order("CAGF")
def_card(67, "Mexican Traffickers")
def_card(68, "Narco-Subs")
def_card(69, "Riverines & Fast Boats")
-def_initiative("CAFG")
+def_order("CAFG")
def_card(70, "Ayahuasca Tourism")
def_card(71, "Darién")
def_card(72, "Sicarios")
-initiative = null
+order = null
def_card(73, "Propaganda!")
def_card(74, "Propaganda!")
def_card(75, "Propaganda!")
def_card(76, "Propaganda!")
-console.log("const cards = {")
-for (let c of cards) console.log(JSON.stringify(c) + ",")
-console.log("}")
-
let spaces = [ ]
let space_names = [ ]
@@ -124,12 +126,12 @@ function add(list, item) {
}
function def_space(type, pop, name) {
- spaces.push({ type, name, pop, adjacent: [] })
+ spaces.push({ type, id: to_ascii(name), name, pop, adjacent: [] })
space_names.push(name)
}
function def_town(name) {
- spaces.push({ type: "town", name, adjacent: [] })
+ spaces.push({ type: "town", id: to_ascii(name), name, adjacent: [] })
space_names.push(name)
}
@@ -138,7 +140,7 @@ function def_loc(type, econ, cities, depts) {
let loc_names = cities.concat(depts)
let loc_spaces = loc_names.map(n => space_names.indexOf(n))
let ix = spaces.length
- spaces.push({ type, name, econ, adjacent: loc_spaces.filter(x=>x>0) })
+ spaces.push({ type, name, id: to_ascii(name), econ, adjacent: loc_spaces.filter(x=>x>0) })
for (let loc of loc_spaces)
if (loc >= 0)
add(spaces[loc].adjacent, ix)
@@ -159,12 +161,12 @@ data.first_city = spaces.length
def_space("city", 8, "Bogotá")
def_space("city", 3, "Cali")
+def_space("city", 3, "Medellín")
def_space("city", 2, "Bucaramanga")
def_space("city", 2, "Ibagué")
def_space("city", 2, "Santa Marta")
def_space("city", 1, "Cartagena")
def_space("city", 1, "Cúcuta")
-def_space("city", 1, "Medellín")
def_space("city", 1, "Neiva")
def_space("city", 1, "Pasto")
def_space("city", 1, "Sincelejo")
@@ -382,9 +384,7 @@ data.coastal_spaces = [ "Cesar", "Atlántico", "Chocó", "Nariño" ].map(n=>spac
for (let i = 1; i < spaces.length; ++i)
spaces[i].adjacent.sort((a,b)=>a-b)
-console.log("const spaces = [")
-for (let x of spaces) console.log(JSON.stringify(x) + ",")
-console.log("]")
+data.cards = cards
+data.spaces = spaces
-for (let k in data)
- console.log("const", k, "=", JSON.stringify(data[k]))
+fs.writeFileSync("data.js", "const data = " + JSON.stringify(data, 0, "\t") + "\nif (typeof module !== 'undefined') module.exports = data\n")
diff --git a/tools/genpieces.mjs b/tools/genpieces.mjs
index ec478f6..cf157c5 100644
--- a/tools/genpieces.mjs
+++ b/tools/genpieces.mjs
@@ -282,6 +282,7 @@ border("#80643e", "#token_prop_card")
border("#d74729", "#token_oppose_plus_bases")
border("#004e81", "#token_el_presidente")
border("#6a8796", "#token_aid")
+border("#01904b", ".token.shipment")
border("#4b87c0", ".token.passive_support")
border("#b75f61", ".token.passive_opposition")
border("#0054a6", ".token.active_support")
diff --git a/tools/scale_tokens.sh b/tools/scale_tokens.sh
index 83bffdf..f6ccdb4 100644
--- a/tools/scale_tokens.sh
+++ b/tools/scale_tokens.sh
@@ -1,46 +1,48 @@
mkdir -p tokens.1x tokens.2x
function loop() {
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_1.ppm $OUT/govt_control.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_1.ppm $OUT/farc_control.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_6.ppm $OUT/farc_zone.png
-
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_7.ppm $OUT/unshaded_ndsc.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_8.ppm $OUT/unshaded_ospina.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_15.ppm $OUT/unshaded_tapias.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_16.ppm $OUT/unshaded_1st_div.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_23.ppm $OUT/unshaded_black_hawks.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_24.ppm $OUT/unshaded_7th_sf.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_32.ppm $OUT/unshaded_meteoro.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_40.ppm $OUT/unshaded_mtn_bns.png
-
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_7.ppm $OUT/shaded_ndsc.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_8.ppm $OUT/shaded_ospina.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_15.ppm $OUT/shaded_tapias.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_16.ppm $OUT/shaded_1st_div.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_23.ppm $OUT/shaded_black_hawks.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_24.ppm $OUT/shaded_7th_sf.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_32.ppm $OUT/shaded_meteoro.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_40.ppm $OUT/shaded_mtn_bns.png
-
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_30.ppm $OUT/oppose_plus_bases.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_31.ppm $OUT/aid.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_37.ppm $OUT/el_presidente.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_38.ppm $OUT/total_support.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_39.ppm $OUT/prop_card.png
-
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_1.ppm $OUT/active_support.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_2.ppm $OUT/terror.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_4.ppm $OUT/active_opposition.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_5.ppm $OUT/reminder_senado_cartels.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_6.ppm $OUT/reminder_darien.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_8.ppm $OUT/reminder_senado_auc.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_11.ppm $OUT/reminder_senado_farc.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_17.ppm $OUT/reminder_sucumbios.png
-
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_b_1.ppm $OUT/passive_support.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_b_2.ppm $OUT/sabotage.png
- convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_b_4.ppm $OUT/passive_opposition.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/shipment.png $OUT/shipment.png
+
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_1.png $OUT/govt_control.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_1.png $OUT/farc_control.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_6.png $OUT/farc_zone.png
+
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_7.png $OUT/unshaded_ndsc.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_8.png $OUT/unshaded_ospina.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_15.png $OUT/unshaded_tapias.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_16.png $OUT/unshaded_1st_div.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_23.png $OUT/unshaded_black_hawks.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_24.png $OUT/unshaded_7th_sf.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_32.png $OUT/unshaded_meteoro.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_40.png $OUT/unshaded_mtn_bns.png
+
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_7.png $OUT/shaded_ndsc.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_8.png $OUT/shaded_ospina.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_15.png $OUT/shaded_tapias.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_16.png $OUT/shaded_1st_div.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_23.png $OUT/shaded_black_hawks.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_24.png $OUT/shaded_7th_sf.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_32.png $OUT/shaded_meteoro.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_b_40.png $OUT/shaded_mtn_bns.png
+
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_30.png $OUT/oppose_plus_bases.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_31.png $OUT/aid.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_37.png $OUT/el_presidente.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_38.png $OUT/total_support.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/five_a_39.png $OUT/prop_card.png
+
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_1.png $OUT/active_support.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_2.png $OUT/terror.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_4.png $OUT/active_opposition.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_5.png $OUT/reminder_senado_cartels.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_6.png $OUT/reminder_darien.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_8.png $OUT/reminder_senado_auc.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_11.png $OUT/reminder_senado_farc.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_a_17.png $OUT/reminder_sucumbios.png
+
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_b_1.png $OUT/passive_support.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_b_2.png $OUT/sabotage.png
+ convert -colorspace RGB -resize $SCALE -colorspace sRGB HIRES/tokens/half_b_4.png $OUT/passive_opposition.png
}
OUT=tokens.1x
diff --git a/tools/slice_tokens.sh b/tools/slice_tokens.sh
index 5218792..f9ff041 100644
--- a/tools/slice_tokens.sh
+++ b/tools/slice_tokens.sh
@@ -19,15 +19,20 @@
# 1/2" 576x576+12+12
# 5/8" 720x720+15+15
+# Circles! 825x825 -> trim to 800x800 @75dpi is 50x50
+# 3887+8670 - 4712+9495
+
pngtopnm HIRES/tokens/sheet_a.png > /tmp/big.ppm
+pnmcut 3887 8670 825 825 /tmp/big.ppm | pnmcut 12 12 800 800 | pnmtopng > HIRES/tokens/shipment.png
+
I=1
for L in 592 4192 4792 5392 5992 6592
do
for T in 1360 5260 5860
do
echo half $I $L $T
- pnmcut $L $T 600 600 /tmp/big.ppm | pnmcut 12 12 576 576 | pnmtopng > HIRES/tokens/half_a_$I.ppm
+ pnmcut $L $T 600 600 /tmp/big.ppm | pnmcut 12 12 576 576 | pnmtopng > HIRES/tokens/half_a_$I.png
I=$(expr $I + 1)
done
done
@@ -38,7 +43,7 @@ do
for T in 760 1746 2732 3718 4704 5692 6678 7664
do
echo five $I $L $T
- pnmcut $L $T 750 750 /tmp/big.ppm | pnmcut 15 15 720 720 | pnmtopng > HIRES/tokens/five_a_$I.ppm
+ pnmcut $L $T 750 750 /tmp/big.ppm | pnmcut 15 15 720 720 | pnmtopng > HIRES/tokens/five_a_$I.png
I=$(expr $I + 1)
done
done
@@ -51,7 +56,7 @@ do
for T in 1360 5260 5860
do
echo half $I $L $T
- pnmcut $L $T 600 600 /tmp/big.ppm | pnmcut 12 12 576 576 | pnmtopng > HIRES/tokens/half_b_$I.ppm
+ pnmcut $L $T 600 600 /tmp/big.ppm | pnmcut 12 12 576 576 | pnmtopng > HIRES/tokens/half_b_$I.png
I=$(expr $I + 1)
done
done
@@ -62,7 +67,7 @@ do
for T in 760 1746 2732 3718 4704 5692 6678 7664
do
echo five $I $L $T
- pnmcut $L $T 750 750 /tmp/big.ppm | pnmcut 15 15 720 720 | pnmtopng > HIRES/tokens/five_b_$I.ppm
+ pnmcut $L $T 750 750 /tmp/big.ppm | pnmcut 15 15 720 720 | pnmtopng > HIRES/tokens/five_b_$I.png
I=$(expr $I + 1)
done
done