diff options
-rw-r--r-- | data.js | 3365 | ||||
-rw-r--r-- | play.css | 545 | ||||
-rw-r--r-- | play.html | 120 | ||||
-rw-r--r-- | play.js | 674 | ||||
-rw-r--r-- | rules.js | 387 | ||||
-rw-r--r-- | tools/gendata.js | 488 | ||||
-rw-r--r-- | tools/layout.json | 3728 | ||||
-rw-r--r-- | tools/layout.svg | 343 | ||||
-rw-r--r-- | tools/parse-layout-old.js | 466 | ||||
-rw-r--r-- | tools/parse-layout.js | 325 | ||||
-rw-r--r-- | tools/process-layout.js | 164 | ||||
-rw-r--r-- | tools/rotate_year.sh | 8 |
12 files changed, 8610 insertions, 2003 deletions
@@ -1,828 +1,2545 @@ -"use strict" - -var data - -;(function () { - -function array_insert(array, index, item) { - for (let i = array.length; i > index; --i) - array[i] = array[i - 1] - array[index] = item -} - -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 make_index_from_list(list) { - let map = {} - for (let i = 0; i < list.length; ++i) - map[list[i]] = i - return map -} - -const BOXES = {} - -// === GENERALS === - -const generals = [ - { name: "Arnold", owner: "American", strategy: 1, battle: 3, agility: 2, bonus: false }, - { name: "Gates", owner: "American", strategy: 2, battle: 2, agility: 1, bonus: false }, - { name: "Greene", owner: "American", strategy: 1, battle: 4, agility: 2, bonus: true }, - { name: "Lafayette", owner: "American", strategy: 1, battle: 2, agility: 1, bonus: false }, - { name: "Lee", owner: "American", strategy: 2, battle: 1, agility: 1, bonus: false }, - { name: "Lincoln", owner: "American", strategy: 2, battle: 1, agility: 1, bonus: false }, - { name: "Washington", owner: "American", strategy: 1, battle: 5, agility: 2, bonus: true }, - - { name: "Rochambeau", owner: "French", strategy: 2, battle: 4, agility: 2, bonus: false }, - - { name: "Burgoyne", owner: "British", strategy: 2, battle: 2, agility: 1, bonus: false }, - { name: "Carleton", owner: "British", strategy: 3, battle: 3, agility: 2, bonus: false }, - { name: "Clinton", owner: "British", strategy: 3, battle: 4, agility: 2, bonus: false }, - { name: "Cornwallis", owner: "British", strategy: 2, battle: 4, agility: 2, bonus: false }, - { name: "Howe", owner: "British", strategy: 3, battle: 6, agility: 3, bonus: false }, -] - -const general_index = make_index_from_list(generals.map(x=>x.name)) - -// === SPACES === - -const spaces = [] -const space_index = {} - -const colony_name = [ "Canada", "NH", "NY", "MA", "CT", "RI", "PA", "NJ", "MD", "DE", "VA", "NC", "SC", "GA" ] -const colony_spaces = colony_name.map(x => []) - -const Canada = 0 -const NH = 1 -const NY = 2 -const MA = 3 -const CT = 4 -const RI = 5 -const PA = 6 -const NJ = 7 -const MD = 8 -const DE = 9 -const VA = 10 -const NC = 11 -const SC = 12 -const GA = 13 - -function box(category, A, x, y) { - x += 40 - y += 36 - BOXES[A] = { name: A, category: category, x: x, y: y } -} - -function space(colony, name, x, y, type) { - x += 40 - y += 36 - if (type === "winter-quarters") { - x += 1 - y += 1 - } - - let ix = spaces.length - space_index[name] = ix - spaces.push({ name: name, colony, type, port: 0, path: [], wilderness: [], adjacent: [], x, y }) - set_add(colony_spaces[colony], ix) -} - -function not_space(type, name, x, y) { - // box that's not a space - x += 40 - y += 36 - let ix = spaces.length - space_index[name] = ix - spaces.push({ name, type, x, y }) -} - -function line(A, B, type) { - A = space_index[A] - B = space_index[B] - set_add(spaces[A].adjacent, B) - set_add(spaces[B].adjacent, A) - set_add(spaces[A][type], B) - set_add(spaces[B][type], A) -} - -space(Canada, "Fort Detroit", 55, 673, "regular-space") -space(Canada, "Montreal", 1000, 159, "fortified-port") -space(Canada, "Quebec", 1122, 57, "fortified-port") -space(CT, "Hartford", 1183, 749, "winter-quarters") -space(CT, "New Haven", 1088, 736, "regular-space") -space(DE, "Wilmington DE", 885, 1079, "winter-quarters") -space(GA, "Augusta", 140, 2060, "regular-space") -space(GA, "Savannah", 227, 2271, "regular-space") -space(GA, "St. Mary's", 180, 2394, "regular-space") -space(MA, "Barnstable", 1394, 705, "regular-space") -space(MA, "Boston", 1327, 592, "winter-quarters") -space(MA, "Falmouth", 1334, 410, "regular-space") -space(MA, "Lexington Concord", 1218, 628, "regular-space") -space(MA, "Springfield", 1095, 624, "winter-quarters") -space(MD, "Baltimore", 769, 1105, "winter-quarters") -space(MD, "Fort Cumberland", 446, 1098, "regular-space") -space(MD, "Frederick Town", 646, 1097, "regular-space") -space(NC, "Charlotte", 215, 1653, "regular-space") -space(NC, "Gilbert Town", 123, 1568, "regular-space") -space(NC, "Hillsboro", 484, 1582, "regular-space") -space(NC, "New Bern", 764, 1670, "regular-space") -space(NC, "Salem", 308, 1561, "regular-space") -space(NC, "Wake (Raleigh)", 606, 1671, "regular-space") -space(NC, "Wilmington NC", 676, 1816, "regular-space") -space(NH, "Brattleboro", 1102, 486, "regular-space") -space(NH, "Concord", 1214, 460, "regular-space") -space(NH, "Norwich", 1125, 342, "winter-quarters") -space(NJ, "Monmouth", 998, 1074, "regular-space") -space(NJ, "Morristown", 864, 831, "regular-space") -space(NJ, "New Brunswick", 961, 942, "regular-space") -space(NY, "Albany", 965, 626, "winter-quarters") -space(NY, "Fort Niagara", 461, 526, "regular-space") -space(NY, "Fort Stanwix", 877, 483, "winter-quarters") -space(NY, "Genesee", 668, 582, "regular-space") -space(NY, "Long Island", 1147, 868, "regular-space") -space(NY, "New York", 1024, 838, "winter-quarters") -space(NY, "Oswego", 768, 424, "regular-space") -space(NY, "Saratoga", 992, 496, "regular-space") -space(NY, "Ticonderoga", 993, 355, "winter-quarters") -space(NY, "Westchester", 955, 747, "regular-space") -space(PA, "Bassett Town", 147, 984, "regular-space") -space(PA, "Harrisburg", 555, 880, "regular-space") -space(PA, "Philadelphia", 846, 961, "fortified-port") -space(PA, "Pittsburgh", 439, 958, "winter-quarters") -space(PA, "Reading", 684, 900, "regular-space") -space(PA, "Wyoming Valley", 685, 747, "winter-quarters") -space(PA, "York", 589, 992, "regular-space") -space(RI, "Newport", 1289, 736, "winter-quarters") -space(SC, "Camden", 346, 1773, "regular-space") -space(SC, "Charleston", 444, 2050, "fortified-port") -space(SC, "Cheraw", 496, 1795, "regular-space") -space(SC, "Eutaw Springs", 404, 1905, "regular-space") -space(SC, "Fort Prince George", 74, 1773, "regular-space") -space(SC, "Georgetown", 574, 1915, "regular-space") -space(SC, "Ninety Six", 222, 1932, "regular-space") -space(VA, "Abingdon", 61, 1451, "regular-space") -space(VA, "Alexandria", 653, 1217, "winter-quarters") -space(VA, "Charlottesville", 434, 1257, "winter-quarters") -space(VA, "Fincastle", 349, 1402, "regular-space") -space(VA, "Fort Chiswell", 212, 1408, "regular-space") -space(VA, "Lynch's Ferry", 486, 1444, "regular-space") -space(VA, "Norfolk", 781, 1450, "winter-quarters") -space(VA, "Petersburg", 634, 1446, "regular-space") -space(VA, "Point Pleasant", 132, 1218, "regular-space") -space(VA, "Richmond", 608, 1323, "winter-quarters") -space(VA, "Yorktown", 767, 1317, "regular-space") - -not_space("box", "British Leader Reinforcements", 1425, 1745) -not_space("box", "French Reinforcements", 150, 340) -not_space("box", "American Leader Reinforcements", 400, 310) -not_space("box", "Captured Generals", 1463, 80) -not_space("box", "Continental Congress Dispersed", 545, 350) - -box("SEA", "Sea1", 1277, 65, "blockade") -box("SEA", "Sea2", 1469, 514, "blockade") -box("SEA", "Sea3", 1213, 981, "blockade") -box("SEA", "Sea4", 1058, 1174, "blockade") -box("SEA", "Sea5", 947, 1395, "blockade") -box("SEA", "Sea6", 637, 2027, "blockade") -box("SEA", "Sea7", 359, 2358, "blockade") - -box("ALLIANCE", "French Alliance Track 0", 898, 2240) -box("ALLIANCE", "French Alliance Track 1", 964, 2240) -box("ALLIANCE", "French Alliance Track 2", 1030, 2240) -box("ALLIANCE", "French Alliance Track 3", 1096, 2240) -box("ALLIANCE", "French Alliance Track 4", 1162, 2240) -box("ALLIANCE", "French Alliance Track 5", 1228, 2240) -box("ALLIANCE", "French Alliance Track 6", 1294, 2240) -box("ALLIANCE", "French Alliance Track 7", 1360, 2240) -box("ALLIANCE", "French Alliance Track 8", 1426, 2240) -box("ALLIANCE", "French Alliance Track 9", 1492, 2240) - -box("TURN", "Game Turn 1775", 898, 2370) -box("TURN", "Game Turn 1776", 964, 2370) -box("TURN", "Game Turn 1777", 1030, 2370) -box("TURN", "Game Turn 1778", 1096, 2370) -box("TURN", "Game Turn 1779", 1162, 2370) -box("TURN", "Game Turn 1780", 1228, 2370) -box("TURN", "Game Turn 1781", 1294, 2370) -box("TURN", "Game Turn 1782", 1360, 2370) -box("TURN", "Game Turn 1783", 1426, 2370) - -box("CONTROL", "Canada", 1376, 967) -box("CONTROL", "NH", 1458, 1006) -box("CONTROL", "NY", 1375, 1081) -box("CONTROL", "MA", 1491, 1081) -box("CONTROL", "CT", 1455, 1156) -box("CONTROL", "RI", 1533, 1156) -box("CONTROL", "PA", 1299, 1194) -box("CONTROL", "NJ", 1376, 1189) -box("CONTROL", "MD", 1299, 1266) -box("CONTROL", "DE", 1376, 1266) -box("CONTROL", "VA", 1241, 1337) -box("CONTROL", "NC", 1260, 1407) -box("CONTROL", "SC", 1212, 1479) -box("CONTROL", "GA", 1164, 1550) - -function wilderness(A, B) { - line(A, B, "wilderness") -} - -function path(A, B) { - line(A, B, "path") -} - -function sea(space, zone) { - spaces[space_index[space]].port = zone -} - -wilderness("Quebec", "Falmouth") -wilderness("Fort Detroit", "Bassett Town") -wilderness("Bassett Town", "Point Pleasant") - -path("Quebec", "Montreal") -path("Montreal", "Ticonderoga") -path("Montreal", "Oswego") -path("Oswego", "Fort Niagara") -path("Oswego", "Fort Stanwix") -path("Fort Niagara", "Fort Detroit") -path("Fort Niagara", "Genesee") -path("Ticonderoga", "Norwich") -path("Ticonderoga", "Saratoga") -path("Norwich", "Brattleboro") -path("Norwich", "Concord") -path("Concord", "Falmouth") -path("Concord", "Brattleboro") -path("Saratoga", "Brattleboro") -path("Saratoga", "Albany") -path("Brattleboro", "Springfield") -path("Fort Stanwix", "Albany") -path("Albany", "Springfield") -path("Lexington Concord", "Springfield") -path("Lexington Concord", "Boston") -path("Falmouth", "Boston") -path("Barnstable", "Boston") -path("Newport", "Boston") -path("Newport", "Hartford") -path("Springfield", "Hartford") -path("New Haven", "Hartford") -path("New Haven", "New York") -path("New Haven", "Westchester") -path("New York", "Westchester") -path("New York", "Long Island") -path("Newport", "Lexington Concord") -path("Albany", "Westchester") -path("Fort Stanwix", "Genesee") -path("Genesee", "Wyoming Valley") -path("Fort Niagara", "Pittsburgh") -path("Morristown", "Westchester") -path("Morristown", "Wyoming Valley") -path("Morristown", "Reading") -path("Morristown", "New Brunswick") -path("Morristown", "New York") -path("New York", "New Brunswick") -path("Monmouth", "New Brunswick") -path("Philadelphia", "New Brunswick") -path("Monmouth", "Philadelphia") -path("Reading", "Philadelphia") -path("Wyoming Valley", "Reading") -path("Wilmington DE", "Philadelphia") -path("Wilmington DE", "Baltimore") -path("Bassett Town", "Pittsburgh") -path("Fort Cumberland", "Pittsburgh") -path("Harrisburg", "Pittsburgh") -path("Harrisburg", "York") -path("Reading", "York") -path("Harrisburg", "Reading") -path("Frederick Town", "York") -path("Frederick Town", "Baltimore") -path("Frederick Town", "Alexandria") -path("Frederick Town", "Charlottesville") -path("Frederick Town", "Fort Cumberland") -path("Richmond", "Alexandria") -path("Richmond", "Yorktown") -path("Richmond", "Petersburg") -path("Richmond", "Lynch's Ferry") -path("Richmond", "Charlottesville") -path("Richmond", "Norfolk") -path("Fincastle", "Charlottesville") -path("Fincastle", "Lynch's Ferry") -path("Fincastle", "Fort Chiswell") -path("Point Pleasant", "Fort Chiswell") -path("Abingdon", "Fort Chiswell") -path("Abingdon", "Gilbert Town") -path("Fort Prince George", "Gilbert Town") -path("Charlotte", "Gilbert Town") -path("Charlotte", "Salem") -path("Hillsboro", "Salem") -path("Cheraw", "Salem") -path("Lynch's Ferry", "Salem") -path("Charlotte", "Camden") -path("Abingdon", "Fort Prince George") -path("New Bern", "Norfolk") -path("Petersburg", "Norfolk") -path("Petersburg", "Wake (Raleigh)") -path("New Bern", "Wake (Raleigh)") -path("Hillsboro", "Wake (Raleigh)") -path("Petersburg", "Hillsboro") -path("Petersburg", "Lynch's Ferry") -path("Wilmington NC", "Wake (Raleigh)") -path("Wilmington NC", "Cheraw") -path("Wilmington NC", "Georgetown") -path("Cheraw", "Georgetown") -path("Cheraw", "Camden") -path("Ninety Six", "Fort Prince George") -path("Ninety Six", "Camden") -path("Ninety Six", "Augusta") -path("Eutaw Springs", "Camden") -path("Eutaw Springs", "Charleston") -path("Georgetown", "Charleston") -path("Savannah", "Charleston") -path("Alexandria", "Baltimore") -path("Savannah", "Augusta") -path("Savannah", "St. Mary's") -path("Charlottesville", "Lynch's Ferry") - -sea("Quebec", 1) -sea("Montreal", 1) - -sea("Falmouth", 2) -sea("Boston", 2) -sea("Barnstable", 2) -sea("Newport", 2) - -sea("New Haven", 3) -sea("New York", 3) -sea("Long Island", 3) - -sea("Philadelphia", 4) -sea("Wilmington DE", 4) - -sea("Baltimore", 5) -sea("Alexandria", 5) -sea("Yorktown", 5) -sea("Norfolk", 5) - -sea("New Bern", 6) -sea("Wilmington NC", 6) -sea("Charleston", 6) - -sea("Savannah", 7) -sea("St. Mary's", 7) - -// === CARDS === - -const CARD_3_OPS = { title: "3 OPS", type: "ops", count: 3 } -const CARD_2_OPS = { title: "2 OPS", type: "ops", count: 2 } -const CARD_1_OPS = { title: "1 OPS", type: "ops", count: 1 } - -const cards = [ - null, - - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - CARD_3_OPS, - - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - CARD_2_OPS, - - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - CARD_1_OPS, - - /* 67 */ - { - title: "Minor Campaign", - type: "campaign", - count: 2, - }, - - /* 68 */ - { - title: "Minor Campaign", - type: "campaign", - count: 2, - }, - - /* 69 */ - { - title: "Minor Campaign", - type: "campaign", - count: 2, - }, - - /* 70 */ - { - title: "Major Campaign", - type: "campaign", - count: 3, - }, - - /* 71 */ - { - full_title: "North's Government Falls - The War Ends in 1779", - title: "The War Ends in 1779", - type: "mandatory-event", - event: "the_war_ends", - year: 1779, - }, - - /* 72 */ - { - full_title: "North's Government Falls - The War Ends in 1780", - title: "The War Ends in 1780", - type: "mandatory-event", - event: "the_war_ends", - year: 1780, - }, - - /* 73 */ - { - full_title: "North's Government Falls - The War Ends in 1781", - title: "The War Ends in 1781", - type: "mandatory-event", - event: "the_war_ends", - year: 1781, - }, - - /* 74 */ - { - full_title: "North's Government Falls - The War Ends in 1782", - title: "The War Ends in 1782", - type: "mandatory-event", - event: "the_war_ends", - year: 1782, - }, - - /* 75 */ - { - full_title: "North's Government Falls - The War Ends in 1783", - title: "The War Ends in 1783", - type: "mandatory-event", - event: "the_war_ends", - year: 1783, - }, - - /* 76 */ - { - title: "Henry Know Continental Artillery Commander", - type: "american-battle", - }, - - /* 77 */ - { - title: "Jane McCrea Indian Atrocity Sparks Outrage", - type: "american-event", - event: "place_american_pc", - count: 2, - once: true, - }, - - /* 78 */ - { - title: "Iroquois Uprising!", - type: "british-event", - event: "remove_american_pc_from_non_port", - where: [ NH, NY, PA ], - count: 2, - }, - - /* 79 */ - { - title: "Joseph Brant Leads an Iroquois Raid", - type: "british-event", - event: "remove_american_pc_from_non_port", - where: [ NH, NY, PA ], - count: 2, - }, - - /* 80 */ - { - title: "Lt. Colonel Simcoe's Queen's Rangers", - type: "british-event", - event: "remove_american_pc_within_two_spaces_of_a_british_general", - count: 2, - }, - - /* 81 */ - { - title: "D'Estaing Sails for the Caribbean", - type: "british-event", - when: "after_french_alliance", - event: "remove_french_navy", - }, - - /* 82 */ - { - title: "Banastre Tarleton's Waxhaws Massacre", - type: "british-event-or-battle", - event: "remove_american_cu", - }, - - /* 83 */ - { - title: "Lord George Germaine Offers Royal Amnesty", - type: "british-event", - event: "remove_american_pc", - count: 2, - }, - - /* 84 */ - { - title: "George Rogers Clark Leads a Western Offensive", - type: "american-event", - event: "remove_random_british_card", - }, - - /* 85 */ - { - title: "Don Bernardo Galvez Captures Pensacola", - type: "american-event", - when: "european_war_in_effect", - event: "remove_british_cu", - count: 2, - }, - - /* 86 */ - { - title: "Baron von Steuben Trains the Continental Army", - type: "american-event", - event: "baron_von_steuben_trains_the_continental_army", - }, - - /* 87 */ - { - title: "Lord North Offers a Royal Amnesty", - type: "british-event", - event: "remove_american_pc", - count: 4, - once: true, - }, - - /* 88 */ - { - title: "The Swamp Fox, Francis Marion", - type: "american-event", - event: "remove_british_pc_from", - where: [ NC, SC, GA ], - count: 2, - }, - - /* 89 */ - { - title: "The Gamecock: Thomas Sumter", - type: "american-event", - event: "remove_british_pc_from", - where: [ NC, SC, GA ], - count: 2, - }, - - /* 90 */ - { - title: "Josiah Martin Rallies North Carolina Loyalists", - type: "british-event", - event: "remove_american_pc_from", - where: [ NC ], - count: 2, - }, - - /* 91 */ - { - title: 'Thomas Paine Publishes Pamphlets "Common Sense" and "The American Crisis"', - type: "american-event", - event: "place_american_pc_in", - where: [ NH, NY, MA, CT, RI, PA, NJ, MD, DE, VA, NC, SC, GA ], - count: 3, - once: true, - }, - - /* 92 */ - { - title: "Nathan Hale, American Martyr", - type: "american-event", - event: "place_american_pc", - count: 2, - once: true, - }, - - /* 93 */ - { - title: "John Glover's Marblehead Regiment", - type: "american-event", - event: "john_glovers_marblehead_regiment", - }, - - /* 94 */ - { - title: "Pennsylvania and New Jersey Line Mutinies", - type: "british-event", - event: "pennsylvania_and_new_jersey_line_mutinies", - }, - - /* 95 */ - { - title: "William Pitt Urges Peace Talks", - type: "british-event", - when: "before_french_alliance", - event: "remove_american_pc", - count: 2, - reshuffle: "if_played", - }, - - /* 96 */ - { - title: "Hortelez et Cie: Clandestine French and Spanish Aid", - type: "american-event", - when: "before_french_alliance", - event: "advance_french_alliance", - reshuffle: "if_discarded", - count: 2, - }, - - /* 97 */ - { - title: "Admiral Suffren Wins a Naval Victory", - type: "american-event", - when: "after_french_alliance", - event: "remove_random_british_card", - }, - - /* 98 */ - { - title: '"Mad" Anthony Wayne', - type: "american-battle", - }, - - /* 99 */ - { - title: "Declaration of Independence", - type: "mandatory-event", - event: "declaration_of_independence", - once: true, - reshuffle: "if_played", - tournament: true, - }, - - /* 100 */ - { - title: "Benedict Arnold's Treason Undermines the Patriot Cause", - type: "british-battle", - event: "remove_benedict_arnold", - once: true, - }, - - /* 101 */ - { - title: "Benjamin Franklin: Minister to France", - type: "mandatory-event", - event: "advance_french_alliance", - count: 4, - once: true, - tournament: true, - }, - - /* 102 */ - { - title: "Admiral Rodney Captures St. Eustatius", - type: "british-event", - event: "remove_random_american_card", - when: "european_war_in_effect", - once: true, - }, - - /* 103 */ - { - title: "Thaddeus Kosciuszco Constructs Engineering Works", - type: "american-battle", - }, - - /* 104 */ - { - title: "Light Horse Harry Lee", - type: "american-battle", - }, - - /* 105 */ - { - title: "Morgan's Riflemen", - type: "american-battle", - }, - - /* 106 */ - { - title: "John Paul Jones' Shipping Raids", - type: "american-event", - event: "remove_random_british_card", - }, - - /* 107 */ - { - title: "British Light Infantry", - type: "british-battle", - }, - - /* 108 */ - { - title: "Lord Sandwich Coastal Raids", - type: "british-event", - event: "lord_sandwich_coastal_raids", +const data = { + "layout": { + "sea": [ + [ + 1268, + 60, + 97, + 83 + ], + [ + 1461, + 508, + 97, + 84 + ], + [ + 1205, + 975, + 97, + 83 + ], + [ + 1049, + 1169, + 97, + 83 + ], + [ + 938, + 1389, + 97, + 83 + ], + [ + 629, + 2022, + 97, + 83 + ], + [ + 351, + 2352, + 97, + 83 + ] + ], + "colony": [ + [ + 1416, + 1004 + ], + [ + 1498, + 1043 + ], + [ + 1415, + 1118 + ], + [ + 1532, + 1117 + ], + [ + 1495, + 1192 + ], + [ + 1573, + 1192 + ], + [ + 1339, + 1230 + ], + [ + 1416, + 1225 + ], + [ + 1339, + 1303 + ], + [ + 1416, + 1303 + ], + [ + 1282, + 1373 + ], + [ + 1300, + 1444 + ], + [ + 1252, + 1515 + ], + [ + 1204, + 1587 + ] + ], + "french_alliance_track": { + "0": [ + 937, + 2277 + ], + "1": [ + 1003, + 2277 + ], + "2": [ + 1069, + 2277 + ], + "3": [ + 1135, + 2277 + ], + "4": [ + 1202, + 2277 + ], + "5": [ + 1268, + 2277 + ], + "6": [ + 1334, + 2277 + ], + "7": [ + 1400, + 2277 + ], + "8": [ + 1467, + 2277 + ], + "9": [ + 1533, + 2277 + ] + }, + "turn": { + "1775": [ + 936, + 2404 + ], + "1776": [ + 1002, + 2404 + ], + "1777": [ + 1068, + 2404 + ], + "1778": [ + 1134, + 2404 + ], + "1779": [ + 1201, + 2404 + ], + "1780": [ + 1267, + 2404 + ], + "1781": [ + 1333, + 2404 + ], + "1782": [ + 1399, + 2404 + ], + "1783": [ + 1465, + 2404 + ] + }, + "card_box": { + "BRC": [ + 1004, + 2054 + ], + "ARC1": [ + 1237, + 2054 + ], + "ARC2": [ + 1470, + 2054 + ], + "War Ends Card": [ + 1166, + 1778 + ] + }, + "box": { + "British Reinforcement Box": [ + 1358, + 1673, + 214, + 206 + ], + "American Leader Reinforcements": [ + 294, + 258, + 214, + 206 + ], + "French Reinforcements": [ + 63, + 258, + 214, + 207 + ], + "Continental Congress Dispersed": [ + 542, + 332, + 88, + 104 + ], + "Captured Generals": [ + 1400, + 46, + 205, + 136 + ] + } }, - - /* 109 */ - { - title: "Edward Bancroft, British Double Agent", - type: "british-event", - event: "remove_random_american_card", + "spaces": [ + { + "name": "Charleston", + "type": "fortified_port", + "adjacent": [ + 30, + 40, + 59 + ], + "path": [ + 30, + 40, + 59 + ], + "wilderness": [], + "port": 5, + "colony": 12, + "x": 484, + "y": 2086 + }, + { + "name": "Montreal", + "type": "fortified_port", + "adjacent": [ + 3, + 18, + 53 + ], + "path": [ + 3, + 18, + 53 + ], + "wilderness": [], + "port": 0, + "colony": 0, + "x": 1040, + "y": 195 + }, + { + "name": "Philadelphia", + "type": "fortified_port", + "adjacent": [ + 19, + 47, + 50, + 56 + ], + "path": [ + 19, + 47, + 50, + 56 + ], + "wilderness": [], + "port": 3, + "colony": 6, + "x": 886, + "y": 998 + }, + { + "name": "Quebec", + "type": "fortified_port", + "adjacent": [ + 1, + 31 + ], + "path": [ + 1 + ], + "wilderness": [ + 31 + ], + "port": 0, + "colony": 0, + "x": 1163, + "y": 94 + }, + { + "name": "Albany", + "type": "winter_quarters", + "adjacent": [ + 9, + 17, + 58, + 62 + ], + "path": [ + 9, + 17, + 58, + 62 + ], + "wilderness": [], + "port": -1, + "colony": 2, + "x": 1007, + "y": 664 + }, + { + "name": "Alexandria", + "type": "winter_quarters", + "adjacent": [ + 6, + 16, + 38 + ], + "path": [ + 6, + 16, + 38 + ], + "wilderness": [], + "port": 4, + "colony": 10, + "x": 694, + "y": 1254 + }, + { + "name": "Baltimore", + "type": "winter_quarters", + "adjacent": [ + 5, + 19, + 38 + ], + "path": [ + 5, + 19, + 38 + ], + "wilderness": [], + "port": 4, + "colony": 8, + "x": 811, + "y": 1143 + }, + { + "name": "Boston", + "type": "winter_quarters", + "adjacent": [ + 12, + 23, + 31, + 44 + ], + "path": [ + 12, + 23, + 31, + 44 + ], + "wilderness": [], + "port": 1, + "colony": 3, + "x": 1368, + "y": 629 + }, + { + "name": "Charlottesville", + "type": "winter_quarters", + "adjacent": [ + 16, + 32, + 38, + 46 + ], + "path": [ + 16, + 32, + 38, + 46 + ], + "wilderness": [], + "port": -1, + "colony": 10, + "x": 475, + "y": 1294 + }, + { + "name": "Fort Stanwix", + "type": "winter_quarters", + "adjacent": [ + 4, + 39, + 53 + ], + "path": [ + 4, + 39, + 53 + ], + "wilderness": [], + "port": -1, + "colony": 2, + "x": 918, + "y": 521 + }, + { + "name": "Hartford", + "type": "winter_quarters", + "adjacent": [ + 10, + 12, + 17 + ], + "path": [ + 10, + 12, + 17 + ], + "wilderness": [], + "port": -1, + "colony": 4, + "x": 1224, + "y": 786 + }, + { + "name": "New York", + "type": "winter_quarters", + "adjacent": [ + 45, + 48, + 50, + 51, + 62 + ], + "path": [ + 45, + 48, + 50, + 51, + 62 + ], + "wilderness": [], + "port": 2, + "colony": 2, + "x": 1065, + "y": 875 + }, + { + "name": "Newport", + "type": "winter_quarters", + "adjacent": [ + 7, + 10, + 44 + ], + "path": [ + 7, + 10, + 44 + ], + "wilderness": [], + "port": 1, + "colony": 5, + "x": 1330, + "y": 773 + }, + { + "name": "Norfolk", + "type": "winter_quarters", + "adjacent": [ + 16, + 49, + 54 + ], + "path": [ + 16, + 49, + 54 + ], + "wilderness": [], + "port": 4, + "colony": 10, + "x": 822, + "y": 1488 + }, + { + "name": "Norwich", + "type": "winter_quarters", + "adjacent": [ + 18, + 25, + 29 + ], + "path": [ + 18, + 25, + 29 + ], + "wilderness": [], + "port": -1, + "colony": 1, + "x": 1166, + "y": 379 + }, + { + "name": "Pittsburgh", + "type": "winter_quarters", + "adjacent": [ + 24, + 34, + 36, + 42 + ], + "path": [ + 24, + 34, + 36, + 42 + ], + "wilderness": [], + "port": -1, + "colony": 6, + "x": 480, + "y": 995 + }, + { + "name": "Richmond", + "type": "winter_quarters", + "adjacent": [ + 5, + 8, + 13, + 46, + 54, + 65 + ], + "path": [ + 5, + 8, + 13, + 46, + 54, + 65 + ], + "wilderness": [], + "port": -1, + "colony": 10, + "x": 649, + "y": 1360 + }, + { + "name": "Springfield", + "type": "winter_quarters", + "adjacent": [ + 4, + 10, + 25, + 44 + ], + "path": [ + 4, + 10, + 25, + 44 + ], + "wilderness": [], + "port": -1, + "colony": 3, + "x": 1137, + "y": 661 + }, + { + "name": "Ticonderoga", + "type": "winter_quarters", + "adjacent": [ + 1, + 14, + 58 + ], + "path": [ + 1, + 14, + 58 + ], + "wilderness": [], + "port": -1, + "colony": 2, + "x": 1035, + "y": 393 + }, + { + "name": "Wilmington DE", + "type": "winter_quarters", + "adjacent": [ + 2, + 6 + ], + "path": [ + 2, + 6 + ], + "wilderness": [], + "port": 3, + "colony": 9, + "x": 926, + "y": 1117 + }, + { + "name": "Wyoming Valley", + "type": "winter_quarters", + "adjacent": [ + 39, + 48, + 56 + ], + "path": [ + 39, + 48, + 56 + ], + "wilderness": [], + "port": -1, + "colony": 6, + "x": 726, + "y": 784 + }, + { + "name": "Abingdon", + "type": "regular", + "adjacent": [ + 33, + 37, + 41 + ], + "path": [ + 33, + 37, + 41 + ], + "wilderness": [], + "port": -1, + "colony": 10, + "x": 101, + "y": 1487 + }, + { + "name": "Augusta", + "type": "regular", + "adjacent": [ + 52, + 59 + ], + "path": [ + 52, + 59 + ], + "wilderness": [], + "port": -1, + "colony": 13, + "x": 180, + "y": 2096 + }, + { + "name": "Barnstable", + "type": "regular", + "adjacent": [ + 7 + ], + "path": [ + 7 + ], + "wilderness": [], + "port": 1, + "colony": 3, + "x": 1434, + "y": 741 + }, + { + "name": "Bassett Town", + "type": "regular", + "adjacent": [ + 15, + 35, + 55 + ], + "path": [ + 15 + ], + "wilderness": [ + 35, + 55 + ], + "port": -1, + "colony": 6, + "x": 187, + "y": 1020 + }, + { + "name": "Brattleboro", + "type": "regular", + "adjacent": [ + 14, + 17, + 29, + 58 + ], + "path": [ + 14, + 17, + 29, + 58 + ], + "wilderness": [], + "port": -1, + "colony": 1, + "x": 1141, + "y": 522 + }, + { + "name": "Camden", + "type": "regular", + "adjacent": [ + 27, + 28, + 30, + 52 + ], + "path": [ + 27, + 28, + 30, + 52 + ], + "wilderness": [], + "port": -1, + "colony": 12, + "x": 386, + "y": 1809 + }, + { + "name": "Charlotte", + "type": "regular", + "adjacent": [ + 26, + 41, + 57 + ], + "path": [ + 26, + 41, + 57 + ], + "wilderness": [], + "port": -1, + "colony": 11, + "x": 255, + "y": 1689 + }, + { + "name": "Cheraw", + "type": "regular", + "adjacent": [ + 26, + 40, + 57, + 63 + ], + "path": [ + 26, + 40, + 57, + 63 + ], + "wilderness": [], + "port": -1, + "colony": 12, + "x": 536, + "y": 1831 + }, + { + "name": "Concord", + "type": "regular", + "adjacent": [ + 14, + 25, + 31 + ], + "path": [ + 14, + 25, + 31 + ], + "wilderness": [], + "port": -1, + "colony": 1, + "x": 1254, + "y": 497 + }, + { + "name": "Eutaw Springs", + "type": "regular", + "adjacent": [ + 0, + 26 + ], + "path": [ + 0, + 26 + ], + "wilderness": [], + "port": -1, + "colony": 12, + "x": 444, + "y": 1942 + }, + { + "name": "Falmouth", + "type": "regular", + "adjacent": [ + 3, + 7, + 29 + ], + "path": [ + 7, + 29 + ], + "wilderness": [ + 3 + ], + "port": 1, + "colony": 3, + "x": 1374, + "y": 446 + }, + { + "name": "Fincastle", + "type": "regular", + "adjacent": [ + 8, + 33, + 46 + ], + "path": [ + 8, + 33, + 46 + ], + "wilderness": [], + "port": -1, + "colony": 10, + "x": 389, + "y": 1438 + }, + { + "name": "Fort Chiswell", + "type": "regular", + "adjacent": [ + 21, + 32, + 55 + ], + "path": [ + 21, + 32, + 55 + ], + "wilderness": [], + "port": -1, + "colony": 10, + "x": 252, + "y": 1444 + }, + { + "name": "Fort Cumberland", + "type": "regular", + "adjacent": [ + 15, + 38 + ], + "path": [ + 15, + 38 + ], + "wilderness": [], + "port": -1, + "colony": 8, + "x": 487, + "y": 1134 + }, + { + "name": "Fort Detroit", + "type": "regular", + "adjacent": [ + 24, + 36 + ], + "path": [ + 36 + ], + "wilderness": [ + 24 + ], + "port": -1, + "colony": 0, + "x": 95, + "y": 708 + }, + { + "name": "Fort Niagara", + "type": "regular", + "adjacent": [ + 15, + 35, + 39, + 53 + ], + "path": [ + 15, + 35, + 39, + 53 + ], + "wilderness": [], + "port": -1, + "colony": 2, + "x": 502, + "y": 562 + }, + { + "name": "Fort Prince George", + "type": "regular", + "adjacent": [ + 21, + 41, + 52 + ], + "path": [ + 21, + 41, + 52 + ], + "wilderness": [], + "port": -1, + "colony": 12, + "x": 114, + "y": 1809 + }, + { + "name": "Frederick Town", + "type": "regular", + "adjacent": [ + 5, + 6, + 8, + 34, + 64 + ], + "path": [ + 5, + 6, + 8, + 34, + 64 + ], + "wilderness": [], + "port": -1, + "colony": 8, + "x": 686, + "y": 1133 + }, + { + "name": "Genesee", + "type": "regular", + "adjacent": [ + 9, + 20, + 36 + ], + "path": [ + 9, + 20, + 36 + ], + "wilderness": [], + "port": -1, + "colony": 2, + "x": 708, + "y": 618 + }, + { + "name": "Georgetown", + "type": "regular", + "adjacent": [ + 0, + 28, + 63 + ], + "path": [ + 0, + 28, + 63 + ], + "wilderness": [], + "port": -1, + "colony": 12, + "x": 614, + "y": 1951 + }, + { + "name": "Gilbert Town", + "type": "regular", + "adjacent": [ + 21, + 27, + 37 + ], + "path": [ + 21, + 27, + 37 + ], + "wilderness": [], + "port": -1, + "colony": 11, + "x": 163, + "y": 1604 + }, + { + "name": "Harrisburg", + "type": "regular", + "adjacent": [ + 15, + 56, + 64 + ], + "path": [ + 15, + 56, + 64 + ], + "wilderness": [], + "port": -1, + "colony": 6, + "x": 595, + "y": 916 + }, + { + "name": "Hillsboro", + "type": "regular", + "adjacent": [ + 54, + 57, + 61 + ], + "path": [ + 54, + 57, + 61 + ], + "wilderness": [], + "port": -1, + "colony": 11, + "x": 524, + "y": 1618 + }, + { + "name": "Lexington Concord", + "type": "regular", + "adjacent": [ + 7, + 12, + 17 + ], + "path": [ + 7, + 12, + 17 + ], + "wilderness": [], + "port": -1, + "colony": 3, + "x": 1258, + "y": 664 + }, + { + "name": "Long Island", + "type": "regular", + "adjacent": [ + 11 + ], + "path": [ + 11 + ], + "wilderness": [], + "port": 2, + "colony": 2, + "x": 1186, + "y": 904 + }, + { + "name": "Lynch's Ferry", + "type": "regular", + "adjacent": [ + 8, + 16, + 32, + 54, + 57 + ], + "path": [ + 8, + 16, + 32, + 54, + 57 + ], + "wilderness": [], + "port": -1, + "colony": 10, + "x": 526, + "y": 1480 + }, + { + "name": "Monmouth", + "type": "regular", + "adjacent": [ + 2, + 50 + ], + "path": [ + 2, + 50 + ], + "wilderness": [], + "port": -1, + "colony": 7, + "x": 1037, + "y": 1110 + }, + { + "name": "Morristown", + "type": "regular", + "adjacent": [ + 11, + 20, + 50, + 56, + 62 + ], + "path": [ + 11, + 20, + 50, + 56, + 62 + ], + "wilderness": [], + "port": -1, + "colony": 7, + "x": 904, + "y": 867 + }, + { + "name": "New Bern", + "type": "regular", + "adjacent": [ + 13, + 61 + ], + "path": [ + 13, + 61 + ], + "wilderness": [], + "port": 5, + "colony": 11, + "x": 804, + "y": 1706 + }, + { + "name": "New Brunswick", + "type": "regular", + "adjacent": [ + 2, + 11, + 47, + 48 + ], + "path": [ + 2, + 11, + 47, + 48 + ], + "wilderness": [], + "port": -1, + "colony": 7, + "x": 1001, + "y": 978 + }, + { + "name": "New Haven", + "type": "regular", + "adjacent": [ + 11, + 62 + ], + "path": [ + 11, + 62 + ], + "wilderness": [], + "port": 2, + "colony": 4, + "x": 1128, + "y": 772 + }, + { + "name": "Ninety Six", + "type": "regular", + "adjacent": [ + 22, + 26, + 37 + ], + "path": [ + 22, + 26, + 37 + ], + "wilderness": [], + "port": -1, + "colony": 12, + "x": 263, + "y": 1968 + }, + { + "name": "Oswego", + "type": "regular", + "adjacent": [ + 1, + 9, + 36 + ], + "path": [ + 1, + 9, + 36 + ], + "wilderness": [], + "port": -1, + "colony": 2, + "x": 808, + "y": 460 + }, + { + "name": "Petersburg", + "type": "regular", + "adjacent": [ + 13, + 16, + 43, + 46, + 61 + ], + "path": [ + 13, + 16, + 43, + 46, + 61 + ], + "wilderness": [], + "port": -1, + "colony": 10, + "x": 675, + "y": 1482 + }, + { + "name": "Point Pleasant", + "type": "regular", + "adjacent": [ + 24, + 33 + ], + "path": [ + 33 + ], + "wilderness": [ + 24 + ], + "port": -1, + "colony": 10, + "x": 172, + "y": 1254 + }, + { + "name": "Reading", + "type": "regular", + "adjacent": [ + 2, + 20, + 42, + 48, + 64 + ], + "path": [ + 2, + 20, + 42, + 48, + 64 + ], + "wilderness": [], + "port": -1, + "colony": 6, + "x": 724, + "y": 936 + }, + { + "name": "Salem", + "type": "regular", + "adjacent": [ + 27, + 28, + 43, + 46 + ], + "path": [ + 27, + 28, + 43, + 46 + ], + "wilderness": [], + "port": -1, + "colony": 11, + "x": 349, + "y": 1597 + }, + { + "name": "Saratoga", + "type": "regular", + "adjacent": [ + 4, + 18, + 25 + ], + "path": [ + 4, + 18, + 25 + ], + "wilderness": [], + "port": -1, + "colony": 2, + "x": 1032, + "y": 532 + }, + { + "name": "Savannah", + "type": "regular", + "adjacent": [ + 0, + 22, + 60 + ], + "path": [ + 0, + 22, + 60 + ], + "wilderness": [], + "port": 6, + "colony": 13, + "x": 267, + "y": 2307 + }, + { + "name": "St. Mary's", + "type": "regular", + "adjacent": [ + 59 + ], + "path": [ + 59 + ], + "wilderness": [], + "port": 6, + "colony": 13, + "x": 220, + "y": 2430 + }, + { + "name": "Wake (Raleigh)", + "type": "regular", + "adjacent": [ + 43, + 49, + 54, + 63 + ], + "path": [ + 43, + 49, + 54, + 63 + ], + "wilderness": [], + "port": -1, + "colony": 11, + "x": 646, + "y": 1707 + }, + { + "name": "Westchester", + "type": "regular", + "adjacent": [ + 4, + 11, + 48, + 51 + ], + "path": [ + 4, + 11, + 48, + 51 + ], + "wilderness": [], + "port": -1, + "colony": 2, + "x": 995, + "y": 783 + }, + { + "name": "Wilmington", + "type": "regular", + "adjacent": [ + 28, + 40, + 61 + ], + "path": [ + 28, + 40, + 61 + ], + "wilderness": [], + "port": 5, + "colony": 11, + "x": 716, + "y": 1852 + }, + { + "name": "York", + "type": "regular", + "adjacent": [ + 38, + 42, + 56 + ], + "path": [ + 38, + 42, + 56 + ], + "wilderness": [], + "port": -1, + "colony": 6, + "x": 629, + "y": 1028 + }, + { + "name": "Yorktown", + "type": "regular", + "adjacent": [ + 16 + ], + "path": [ + 16 + ], + "wilderness": [], + "port": 4, + "colony": 10, + "x": 807, + "y": 1353 + }, + { + "name": "American Leader Reinforcements", + "type": "box", + "adjacent": [], + "path": [], + "wilderness": [], + "port": -1, + "colony": -1, + "x": 401, + "y": 361 + }, + { + "name": "British Reinforcement Box", + "type": "box", + "adjacent": [], + "path": [], + "wilderness": [], + "port": -1, + "colony": -1, + "x": 1465, + "y": 1776 + }, + { + "name": "Captured Generals", + "type": "box", + "adjacent": [], + "path": [], + "wilderness": [], + "port": -1, + "colony": -1, + "x": 1503, + "y": 114 + }, + { + "name": "Continental Congress Dispersed", + "type": "box", + "adjacent": [], + "path": [], + "wilderness": [], + "port": -1, + "colony": -1, + "x": 586, + "y": 384 + }, + { + "name": "French Reinforcements", + "type": "box", + "adjacent": [], + "path": [], + "wilderness": [], + "port": -1, + "colony": -1, + "x": 170, + "y": 362 + } + ], + "space_index": { + "Charleston": 0, + "Montreal": 1, + "Philadelphia": 2, + "Quebec": 3, + "Albany": 4, + "Alexandria": 5, + "Baltimore": 6, + "Boston": 7, + "Charlottesville": 8, + "Fort Stanwix": 9, + "Hartford": 10, + "New York": 11, + "Newport": 12, + "Norfolk": 13, + "Norwich": 14, + "Pittsburgh": 15, + "Richmond": 16, + "Springfield": 17, + "Ticonderoga": 18, + "Wilmington DE": 19, + "Wyoming Valley": 20, + "Abingdon": 21, + "Augusta": 22, + "Barnstable": 23, + "Bassett Town": 24, + "Brattleboro": 25, + "Camden": 26, + "Charlotte": 27, + "Cheraw": 28, + "Concord": 29, + "Eutaw Springs": 30, + "Falmouth": 31, + "Fincastle": 32, + "Fort Chiswell": 33, + "Fort Cumberland": 34, + "Fort Detroit": 35, + "Fort Niagara": 36, + "Fort Prince George": 37, + "Frederick Town": 38, + "Genesee": 39, + "Georgetown": 40, + "Gilbert Town": 41, + "Harrisburg": 42, + "Hillsboro": 43, + "Lexington Concord": 44, + "Long Island": 45, + "Lynch's Ferry": 46, + "Monmouth": 47, + "Morristown": 48, + "New Bern": 49, + "New Brunswick": 50, + "New Haven": 51, + "Ninety Six": 52, + "Oswego": 53, + "Petersburg": 54, + "Point Pleasant": 55, + "Reading": 56, + "Salem": 57, + "Saratoga": 58, + "Savannah": 59, + "St. Mary's": 60, + "Wake (Raleigh)": 61, + "Westchester": 62, + "Wilmington": 63, + "York": 64, + "Yorktown": 65, + "American Leader Reinforcements": 66, + "British Reinforcement Box": 67, + "Captured Generals": 68, + "Continental Congress Dispersed": 69, + "French Reinforcements": 70 }, - - /* 110 */ - { - title: "Hessian Infantry Bayonet Charge", - type: "british-battle", + "colonies": [ + [ + 1, + 3, + 35 + ], + [ + 14, + 25, + 29 + ], + [ + 4, + 9, + 11, + 18, + 36, + 39, + 45, + 53, + 58, + 62 + ], + [ + 7, + 17, + 23, + 31, + 44 + ], + [ + 10, + 51 + ], + [ + 12 + ], + [ + 2, + 15, + 20, + 24, + 42, + 56, + 64 + ], + [ + 47, + 48, + 50 + ], + [ + 6, + 34, + 38 + ], + [ + 19 + ], + [ + 5, + 8, + 13, + 16, + 21, + 32, + 33, + 46, + 54, + 55, + 65 + ], + [ + 27, + 41, + 43, + 49, + 57, + 61, + 63 + ], + [ + 0, + 26, + 28, + 30, + 37, + 40, + 52 + ], + [ + 22, + 59, + 60 + ] + ], + "seas": [ + [ + 1, + 3 + ], + [ + 7, + 12, + 23, + 31 + ], + [ + 11, + 45, + 51 + ], + [ + 2, + 19 + ], + [ + 5, + 6, + 13, + 65 + ], + [ + 0, + 49, + 63 + ], + [ + 59, + 60 + ] + ], + "generals": [ + { + "name": "Arnold", + "owner": "American", + "strategy": 1, + "battle": 3, + "agility": 2, + "bonus": false + }, + { + "name": "Gates", + "owner": "American", + "strategy": 2, + "battle": 2, + "agility": 1, + "bonus": false + }, + { + "name": "Greene", + "owner": "American", + "strategy": 1, + "battle": 4, + "agility": 2, + "bonus": true + }, + { + "name": "Lafayette", + "owner": "American", + "strategy": 1, + "battle": 2, + "agility": 1, + "bonus": false + }, + { + "name": "Lee", + "owner": "American", + "strategy": 2, + "battle": 1, + "agility": 1, + "bonus": false + }, + { + "name": "Lincoln", + "owner": "American", + "strategy": 2, + "battle": 1, + "agility": 1, + "bonus": false + }, + { + "name": "Washington", + "owner": "American", + "strategy": 1, + "battle": 5, + "agility": 2, + "bonus": true + }, + { + "name": "Rochambeau", + "owner": "French", + "strategy": 2, + "battle": 4, + "agility": 2, + "bonus": false + }, + { + "name": "Burgoyne", + "owner": "British", + "strategy": 2, + "battle": 2, + "agility": 1, + "bonus": false + }, + { + "name": "Carleton", + "owner": "British", + "strategy": 3, + "battle": 3, + "agility": 2, + "bonus": false + }, + { + "name": "Clinton", + "owner": "British", + "strategy": 3, + "battle": 4, + "agility": 2, + "bonus": false + }, + { + "name": "Cornwallis", + "owner": "British", + "strategy": 2, + "battle": 4, + "agility": 2, + "bonus": false + }, + { + "name": "Howe", + "owner": "British", + "strategy": 3, + "battle": 6, + "agility": 3, + "bonus": false + } + ], + "general_index": { + "Arnold": 0, + "Gates": 1, + "Greene": 2, + "Lafayette": 3, + "Lee": 4, + "Lincoln": 5, + "Washington": 6, + "Rochambeau": 7, + "Burgoyne": 8, + "Carleton": 9, + "Clinton": 10, + "Cornwallis": 11, + "Howe": 12 }, -] - -data = { - BOXES, - cards, - general_index, - generals, - space_index, - spaces, - colony_name, - colony_spaces, + "colony_name": [ + "Canada", + "NH", + "NY", + "MA", + "CT", + "RI", + "PA", + "NJ", + "MD", + "DE", + "VA", + "NC", + "SC", + "GA" + ], + "cards": [ + null, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "3 OPS", + "type": "ops", + "count": 3 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "2 OPS", + "type": "ops", + "count": 2 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "1 OPS", + "type": "ops", + "count": 1 + }, + { + "title": "Minor Campaign", + "type": "campaign", + "count": 2 + }, + { + "title": "Minor Campaign", + "type": "campaign", + "count": 2 + }, + { + "title": "Minor Campaign", + "type": "campaign", + "count": 2 + }, + { + "title": "Major Campaign", + "type": "campaign", + "count": 3 + }, + { + "full_title": "North's Government Falls - The War Ends in 1779", + "title": "The War Ends in 1779", + "type": "mandatory-event", + "event": "the_war_ends", + "year": 1779 + }, + { + "full_title": "North's Government Falls - The War Ends in 1780", + "title": "The War Ends in 1780", + "type": "mandatory-event", + "event": "the_war_ends", + "year": 1780 + }, + { + "full_title": "North's Government Falls - The War Ends in 1781", + "title": "The War Ends in 1781", + "type": "mandatory-event", + "event": "the_war_ends", + "year": 1781 + }, + { + "full_title": "North's Government Falls - The War Ends in 1782", + "title": "The War Ends in 1782", + "type": "mandatory-event", + "event": "the_war_ends", + "year": 1782 + }, + { + "full_title": "North's Government Falls - The War Ends in 1783", + "title": "The War Ends in 1783", + "type": "mandatory-event", + "event": "the_war_ends", + "year": 1783 + }, + { + "title": "Henry Know Continental Artillery Commander", + "type": "american-battle" + }, + { + "title": "Jane McCrea Indian Atrocity Sparks Outrage", + "type": "american-event", + "event": "place_american_pc", + "count": 2, + "once": true + }, + { + "title": "Iroquois Uprising!", + "type": "british-event", + "event": "remove_american_pc_from_non_port", + "where": [ + 1, + 2, + 6 + ], + "count": 2 + }, + { + "title": "Joseph Brant Leads an Iroquois Raid", + "type": "british-event", + "event": "remove_american_pc_from_non_port", + "where": [ + 1, + 2, + 6 + ], + "count": 2 + }, + { + "title": "Lt. Colonel Simcoe's Queen's Rangers", + "type": "british-event", + "event": "remove_american_pc_within_two_spaces_of_a_british_general", + "count": 2 + }, + { + "title": "D'Estaing Sails for the Caribbean", + "type": "british-event", + "when": "after_french_alliance", + "event": "remove_french_navy" + }, + { + "title": "Banastre Tarleton's Waxhaws Massacre", + "type": "british-event-or-battle", + "event": "remove_american_cu" + }, + { + "title": "Lord George Germaine Offers Royal Amnesty", + "type": "british-event", + "event": "remove_american_pc", + "count": 2 + }, + { + "title": "George Rogers Clark Leads a Western Offensive", + "type": "american-event", + "event": "remove_random_british_card" + }, + { + "title": "Don Bernardo Galvez Captures Pensacola", + "type": "american-event", + "when": "european_war_in_effect", + "event": "remove_british_cu", + "count": 2 + }, + { + "title": "Baron von Steuben Trains the Continental Army", + "type": "american-event", + "event": "baron_von_steuben_trains_the_continental_army" + }, + { + "title": "Lord North Offers a Royal Amnesty", + "type": "british-event", + "event": "remove_american_pc", + "count": 4, + "once": true + }, + { + "title": "The Swamp Fox, Francis Marion", + "type": "american-event", + "event": "remove_british_pc_from", + "where": [ + 11, + 12, + 13 + ], + "count": 2 + }, + { + "title": "The Gamecock: Thomas Sumter", + "type": "american-event", + "event": "remove_british_pc_from", + "where": [ + 11, + 12, + 13 + ], + "count": 2 + }, + { + "title": "Josiah Martin Rallies North Carolina Loyalists", + "type": "british-event", + "event": "remove_american_pc_from", + "where": [ + 11 + ], + "count": 2 + }, + { + "title": "Thomas Paine Publishes Pamphlets \"Common Sense\" and \"The American Crisis\"", + "type": "american-event", + "event": "place_american_pc_in", + "where": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13 + ], + "count": 3, + "once": true + }, + { + "title": "Nathan Hale, American Martyr", + "type": "american-event", + "event": "place_american_pc", + "count": 2, + "once": true + }, + { + "title": "John Glover's Marblehead Regiment", + "type": "american-event", + "event": "john_glovers_marblehead_regiment" + }, + { + "title": "Pennsylvania and New Jersey Line Mutinies", + "type": "british-event", + "event": "pennsylvania_and_new_jersey_line_mutinies" + }, + { + "title": "William Pitt Urges Peace Talks", + "type": "british-event", + "when": "before_french_alliance", + "event": "remove_american_pc", + "count": 2, + "reshuffle": "if_played" + }, + { + "title": "Hortelez et Cie: Clandestine French and Spanish Aid", + "type": "american-event", + "when": "before_french_alliance", + "event": "advance_french_alliance", + "reshuffle": "if_discarded", + "count": 2 + }, + { + "title": "Admiral Suffren Wins a Naval Victory", + "type": "american-event", + "when": "after_french_alliance", + "event": "remove_random_british_card" + }, + { + "title": "\"Mad\" Anthony Wayne", + "type": "american-battle" + }, + { + "title": "Declaration of Independence", + "type": "mandatory-event", + "event": "declaration_of_independence", + "once": true, + "reshuffle": "if_played", + "tournament": true + }, + { + "title": "Benedict Arnold's Treason Undermines the Patriot Cause", + "type": "british-battle", + "event": "remove_benedict_arnold", + "once": true + }, + { + "title": "Benjamin Franklin: Minister to France", + "type": "mandatory-event", + "event": "advance_french_alliance", + "count": 4, + "once": true, + "tournament": true + }, + { + "title": "Admiral Rodney Captures St. Eustatius", + "type": "british-event", + "event": "remove_random_american_card", + "when": "european_war_in_effect", + "once": true + }, + { + "title": "Thaddeus Kosciuszco Constructs Engineering Works", + "type": "american-battle" + }, + { + "title": "Light Horse Harry Lee", + "type": "american-battle" + }, + { + "title": "Morgan's Riflemen", + "type": "american-battle" + }, + { + "title": "John Paul Jones' Shipping Raids", + "type": "american-event", + "event": "remove_random_british_card" + }, + { + "title": "British Light Infantry", + "type": "british-battle" + }, + { + "title": "Lord Sandwich Coastal Raids", + "type": "british-event", + "event": "lord_sandwich_coastal_raids" + }, + { + "title": "Edward Bancroft, British Double Agent", + "type": "british-event", + "event": "remove_random_american_card" + }, + { + "title": "Hessian Infantry Bayonet Charge", + "type": "british-battle" + } + ] } - -})() - -if (typeof module === "object") - module.exports = data +if (typeof module === 'object') module.exports = data @@ -1,62 +1,5 @@ -main { background-color: slategray; } -header { background-color: silver; } -aside { background-color: #f5e8d7; } -body.America header.your_turn { background-color: skyblue; } -body.Britain header.your_turn { background-color: salmon; } -#role_Britain { background-color: salmon; } -#role_America { background-color: skyblue; } - -#log > div { padding-left: 20px; text-indent: -12px; } - -#log .h1 { - background-color: tan; - font-weight: bold; - text-align: center; - padding: 3px; -} - -#log .h2 { - background-color: tan; - text-align: center; - padding: 1px; -} - -#log .h1, #log .h2 { - border-top: 1px solid black; - border-bottom: 1px solid black; -} - -#log .h2.american { background-color: skyblue; } -#log .h2.british { background-color: salmon; } - -#log .card_tip:hover { text-decoration: dotted underline; } - -:root { - --piece-shadow: 1px 1px 4px #0008; -} - -aside { - width: 230px; -} - -#turn_info { -} - -#turn_info { - background-color: tan; -} - -#turn_info .card { - margin: 5px auto; -} - -.hand { - margin: 15px; - display: flex; - flex-wrap: wrap; - justify-content: center; - min-height: 370px; - gap: 15px; +main { + background-color: slategray; } #tooltip { @@ -72,6 +15,18 @@ aside { display: block; } +#mapwrap { + width: 1650px; + height: 2550px; +} + +#map { + width: 1650px; + height: 2550px; + background-size: 1650px 2550px; + box-shadow: 1px 1px 8px #0008; +} + @media (max-width: 800px) { #tooltip { top: 0; @@ -94,64 +49,80 @@ aside { #popup li.title { text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis } #popup li.disabled { display: none } -/* MAP WITH MARKERS, CUs, LEADERS, AND SPACES */ +#popup li.title { + background-color: moccasin; +} +/* SPACES */ -#mapwrap { - width: 1650px; - height: 2550px; +.space { + position: absolute; + border: 5px solid transparent; } -#map { - width: 1650px; - height: 2550px; - background-size: 1650px 2550px; - box-shadow: 1px 1px 8px #0008; +.space.regular { + border-radius: 50%; +} + +.space.fortified_port { + transform: rotate(45deg); } -#map.hide_markers > div { - visibility: hidden; +.space.sea { + border-radius: 12px; +} + +.space.action { + border-color: white; + box-shadow: 0 0 2px 1px #000c, inset 0 0 2px 1px #000c; +} + +.space.selected { + border-color: aqua; + box-shadow: 0 0 2px 1px #000c, inset 0 0 2px 1px #000c; } +.space.action.Canada { background-color: #e4795080 } +.space.action.NY { background-color: #52954680 } +.space.action.NH { background-color: #ded36380 } +.space.action.MA { background-color: #6e407a80 } +.space.action.CT { background-color: #d6b5be80 } +.space.action.RI { background-color: #31a9d080 } +.space.action.PA { background-color: #64408980 } +.space.action.NJ { background-color: #ca4d5b80 } +.space.action.MD { background-color: #e5a02380 } +.space.action.DE { background-color: #dcca8780 } +.space.action.VA { background-color: #409d8280 } +.space.action.NC { background-color: #d9819480 } +.space.action.SC { background-color: #ffe05d80 } +.space.action.GA { background-color: #6d9ab480 } + /* MARKERS */ .marker { position: absolute; background-size: 100%; border: 1px solid black; - box-shadow: var(--piece-shadow); -} - -.marker.turn { - width: 55px; - height: 55px; -} - -.marker.french-alliance { - width: 55px; - height: 55px; + box-shadow: 1px 1px 4px #0008; } -.marker.congress { +.marker.large { width: 55px; height: 55px; } -.marker.french-navy { +.marker.tall { width: 64px; height: 130px; } -.marker.control { +.marker.small { width: 45px; height: 45px; - visibility: hidden; } -.marker.control.british { visibility: visible; } -.marker.control.american { visibility: visible; } - -.pc { +.marker.pc { + pointer-events: none; border: none; box-shadow: none; width: 58px; @@ -159,130 +130,101 @@ aside { filter: drop-shadow(1px 1px 2px #0008); } -.cu { - width: calc(60px - 6px); - height: 60px; - line-height: 50px; - padding-left: 6px; - font-size: 30px; - color: white; - text-shadow: 0px 0px 3px black; +.marker.cu { + pointer-events: none; + width: calc(60px - 8px - 8px); + height: calc(60px - 14px - 18px); + line-height: 28px; + padding: 14px 8px 18px 8px; + font-size: 28px; font-weight: bold; + color: white; + text-shadow: + -1px -1px 2px #0008, + 1px 1px 2px #0008, + -1px 1px 2px #0008, + 1px -1px 2px #0008 + ; border-radius: 50%; } - -.general.tall { - width: 64px; - height: 130px; -} - -.general.small { - width: 45px; - height: 45px; +.general.action { + box-shadow: 0 0 0 3px yellow, 0 0 2px 4px black; } -.general.offmap { - display: none; +.general.selected { + box-shadow: 0 0 0 3px aqua, 0 0 2px 4px black; } -.general.british.action, .general.british.selected { box-shadow: 0 0 5px 5px white; } -.general.american.action, .general.american.selected { box-shadow: 0 0 5px 5px white; } -.general.french.action, .general.french.selected { box-shadow: 0 0 5px 5px white; } -.general { z-index: 2; } -.general:hover { z-index: 3; } -.general.selected { z-index: 4; } -.marker.congress { z-index: 5 } -.cu { z-index: 1; } -.space { z-index: 6; } +/* CARDS */ -.space { - position: absolute; - border: 7px solid transparent; - display: none; -} -.space.enabled { - display: block; - cursor: pointer; - border: 7px solid transparent; - box-shadow: 0 0 0 4px white, inset 0 0 0 2px white; -} -.space.regular-space { - border-radius: 100px; -} -.space.fortified-port { - transform: rotate(45deg); -} -.space.blockade { - border-radius: 15px; - border: 7px solid white; - box-shadow: none; -} -.space.highlight { - cursor: pointer; - display: block; +#last_played { + margin: 4px auto; } -/* CARDS */ +#hand { + display: flex; + flex-wrap: wrap; + justify-content: center; + min-height: 352px; + padding: 12px; + gap: 12px; + margin: 24px 0; +} .card { width: 250px; height: 350px; - background-size: 100%; border-radius: 16px; + background-size: 100%; border: 1px solid black; - box-shadow: var(--piece-shadow); - display: none; + box-shadow: 1px 1px 4px #0008; } -#turn_info .card { - width: 125px; - height: 175px; - border-radius: 8px; -} - -.card.show { - display: block; -} - -#tooltip.card { - background-position: center; - background-size: 500px 700px; - width: 372px; - height: 524px; - border-radius: 20px; +.card.shrink { + width: 187px; + height: 262px; + border-radius: 12px; } #war_ends { - left:1073px; - top:1647px; - position:absolute; - width: 187px; - height: 262px; - transform: rotate(90deg); - background-size: 187px 262px; + position: absolute; + left: 1034px; + top: 1684px; + + width: 262px; + height: 187px; border-radius: 12px; - display: none; + background-size: 100%; border: 1px solid black; + box-shadow: 1px 1px 4px #0008; + + display: none; } + #war_ends.year_1779 { display: block; } #war_ends.year_1780 { display: block; } #war_ends.year_1781 { display: block; } #war_ends.year_1782 { display: block; } #war_ends.year_1783 { display: block; } -#played_british_reinforcements { left: 909px; } -#played_american_reinforcements_1 { left: 1142px; } -#played_american_reinforcements_2 { left: 1375px; } .reinforcements { - position:absolute; + position: absolute; top: 1922px; + width: 187px; height: 262px; border-radius: 12px; - display: none; border: 1px solid black; + box-shadow: 1px 1px 4px #0008; + + display: none; } + +#played_british_reinforcements { left: 909px; } +#played_american_reinforcements_1 { left: 1142px; } +#played_american_reinforcements_2 { left: 1375px; } + .reinforcements.ops_1 { display: block; } .reinforcements.ops_2 { display: block; } .reinforcements.ops_3 { display: block; } @@ -291,16 +233,18 @@ aside { #map { background-image: url(map75.jpg); } -.marker.french-navy { background-image: url(markers.1x/french_navy_tall.png); } +.marker.french_navy { background-image: url(markers.1x/french_navy.png); } +.marker.french_navy.tall { background-image: url(markers.1x/french_navy_tall.png); } .marker.turn { background-image: url(markers.1x/game_turn_regulars.png); } -.marker.turn.no-regulars { background-image: url(markers.1x/game_turn_no_regulars.png); } -.marker.french-alliance { background-image: url(markers.1x/french_alliance.png); } -.marker.french-alliance.european-war { background-image: url(markers.1x/european_war.png); } +.marker.turn.no_regulars { background-image: url(markers.1x/game_turn_no_regulars.png); } +.marker.french_alliance { background-image: url(markers.1x/french_alliance.png); } +.marker.french_alliance.european_war { background-image: url(markers.1x/european_war.png); } .marker.congress { background-image: url(markers.1x/continental_congress.png); } .marker.congress { background-image: url(markers.1x/continental_congress.png); } .marker.control.british { background-image: url(markers.1x/colony_british.png); } .marker.control.american { background-image: url(markers.1x/colony_american.png); } + .cu.american { background-image: url(markers.1x/cu_american.png); } .cu.british { background-image: url(markers.1x/cu_british.png); } .cu.french { background-image: url(markers.1x/cu_french.png); } @@ -336,125 +280,124 @@ aside { .general.small.Cornwallis { background-image: url(markers.1x/general_cornwallis.png); } .general.small.Howe { background-image: url(markers.1x/general_howe.png); } -#war_ends.year_1779 { background-image:url(cards75/WWcard-071.png); } -#war_ends.year_1780 { background-image:url(cards75/WWcard-072.png); } -#war_ends.year_1781 { background-image:url(cards75/WWcard-073.png); } -#war_ends.year_1782 { background-image:url(cards75/WWcard-074.png); } -#war_ends.year_1783 { background-image:url(cards75/WWcard-075.png); } - -.reinforcements.ops_1 { background-image:url(cards75/WWcard-045.png); } -.reinforcements.ops_2 { background-image:url(cards75/WWcard-023.png); } -.reinforcements.ops_3 { background-image:url(cards75/WWcard-001.png); } - -.card_back{background-image:url(cards100/WW-CardBack.png)} -.card_1{background-image:url(cards100/WWcard-001.png)} -.card_2{background-image:url(cards100/WWcard-001.png)} -.card_3{background-image:url(cards100/WWcard-001.png)} -.card_4{background-image:url(cards100/WWcard-001.png)} -.card_5{background-image:url(cards100/WWcard-001.png)} -.card_6{background-image:url(cards100/WWcard-001.png)} -.card_7{background-image:url(cards100/WWcard-001.png)} -.card_8{background-image:url(cards100/WWcard-001.png)} -.card_9{background-image:url(cards100/WWcard-001.png)} -.card_10{background-image:url(cards100/WWcard-001.png)} -.card_11{background-image:url(cards100/WWcard-001.png)} -.card_12{background-image:url(cards100/WWcard-001.png)} -.card_13{background-image:url(cards100/WWcard-001.png)} -.card_14{background-image:url(cards100/WWcard-001.png)} -.card_15{background-image:url(cards100/WWcard-001.png)} -.card_16{background-image:url(cards100/WWcard-001.png)} -.card_17{background-image:url(cards100/WWcard-001.png)} -.card_18{background-image:url(cards100/WWcard-001.png)} -.card_19{background-image:url(cards100/WWcard-001.png)} -.card_20{background-image:url(cards100/WWcard-001.png)} -.card_21{background-image:url(cards100/WWcard-001.png)} -.card_22{background-image:url(cards100/WWcard-001.png)} -.card_23{background-image:url(cards100/WWcard-023.png)} -.card_24{background-image:url(cards100/WWcard-023.png)} -.card_25{background-image:url(cards100/WWcard-023.png)} -.card_26{background-image:url(cards100/WWcard-023.png)} -.card_27{background-image:url(cards100/WWcard-023.png)} -.card_28{background-image:url(cards100/WWcard-023.png)} -.card_29{background-image:url(cards100/WWcard-023.png)} -.card_30{background-image:url(cards100/WWcard-023.png)} -.card_31{background-image:url(cards100/WWcard-023.png)} -.card_32{background-image:url(cards100/WWcard-023.png)} -.card_33{background-image:url(cards100/WWcard-023.png)} -.card_34{background-image:url(cards100/WWcard-023.png)} -.card_35{background-image:url(cards100/WWcard-023.png)} -.card_36{background-image:url(cards100/WWcard-023.png)} -.card_37{background-image:url(cards100/WWcard-023.png)} -.card_38{background-image:url(cards100/WWcard-023.png)} -.card_39{background-image:url(cards100/WWcard-023.png)} -.card_40{background-image:url(cards100/WWcard-023.png)} -.card_41{background-image:url(cards100/WWcard-023.png)} -.card_42{background-image:url(cards100/WWcard-023.png)} -.card_43{background-image:url(cards100/WWcard-023.png)} -.card_44{background-image:url(cards100/WWcard-023.png)} -.card_45{background-image:url(cards100/WWcard-045.png)} -.card_46{background-image:url(cards100/WWcard-045.png)} -.card_47{background-image:url(cards100/WWcard-045.png)} -.card_48{background-image:url(cards100/WWcard-045.png)} -.card_49{background-image:url(cards100/WWcard-045.png)} -.card_50{background-image:url(cards100/WWcard-045.png)} -.card_51{background-image:url(cards100/WWcard-045.png)} -.card_52{background-image:url(cards100/WWcard-045.png)} -.card_53{background-image:url(cards100/WWcard-045.png)} -.card_54{background-image:url(cards100/WWcard-045.png)} -.card_55{background-image:url(cards100/WWcard-045.png)} -.card_56{background-image:url(cards100/WWcard-045.png)} -.card_57{background-image:url(cards100/WWcard-045.png)} -.card_58{background-image:url(cards100/WWcard-045.png)} -.card_59{background-image:url(cards100/WWcard-045.png)} -.card_60{background-image:url(cards100/WWcard-045.png)} -.card_61{background-image:url(cards100/WWcard-045.png)} -.card_62{background-image:url(cards100/WWcard-045.png)} -.card_63{background-image:url(cards100/WWcard-045.png)} -.card_64{background-image:url(cards100/WWcard-045.png)} -.card_65{background-image:url(cards100/WWcard-045.png)} -.card_66{background-image:url(cards100/WWcard-045.png)} -.card_67{background-image:url(cards100/WWcard-067.png)} -.card_68{background-image:url(cards100/WWcard-067.png)} -.card_69{background-image:url(cards100/WWcard-067.png)} -.card_70{background-image:url(cards100/WWcard-070.png)} -.card_71{background-image:url(cards100/WWcard-071.png)} -.card_72{background-image:url(cards100/WWcard-072.png)} -.card_73{background-image:url(cards100/WWcard-073.png)} -.card_74{background-image:url(cards100/WWcard-074.png)} -.card_75{background-image:url(cards100/WWcard-075.png)} -.card_76{background-image:url(cards100/WWcard-076.png)} -.card_77{background-image:url(cards100/WWcard-077.png)} -.card_78{background-image:url(cards100/WWcard-078.png)} -.card_79{background-image:url(cards100/WWcard-079.png)} -.card_80{background-image:url(cards100/WWcard-080.png)} -.card_81{background-image:url(cards100/WWcard-081.png)} -.card_82{background-image:url(cards100/WWcard-082.png)} -.card_83{background-image:url(cards100/WWcard-083.png)} -.card_84{background-image:url(cards100/WWcard-084.png)} -.card_85{background-image:url(cards100/WWcard-085.png)} -.card_86{background-image:url(cards100/WWcard-086.png)} -.card_87{background-image:url(cards100/WWcard-087.png)} -.card_88{background-image:url(cards100/WWcard-088.png)} -.card_89{background-image:url(cards100/WWcard-089.png)} -.card_90{background-image:url(cards100/WWcard-090.png)} -.card_91{background-image:url(cards100/WWcard-091.png)} -.card_92{background-image:url(cards100/WWcard-092.png)} -.card_93{background-image:url(cards100/WWcard-093.png)} -.card_94{background-image:url(cards100/WWcard-094.png)} -.card_95{background-image:url(cards100/WWcard-095.png)} -.card_96{background-image:url(cards100/WWcard-096.png)} -.card_97{background-image:url(cards100/WWcard-097.png)} -.card_98{background-image:url(cards100/WWcard-098.png)} -.card_99{background-image:url(cards100/WWcard-099.png)} -.card_100{background-image:url(cards100/WWcard-100.png)} -.card_101{background-image:url(cards100/WWcard-101.png)} -.card_102{background-image:url(cards100/WWcard-102.png)} -.card_103{background-image:url(cards100/WWcard-103.png)} -.card_104{background-image:url(cards100/WWcard-104.png)} -.card_105{background-image:url(cards100/WWcard-105.png)} -.card_106{background-image:url(cards100/WWcard-106.png)} -.card_107{background-image:url(cards100/WWcard-107.png)} -.card_108{background-image:url(cards100/WWcard-108.png)} -.card_109{background-image:url(cards100/WWcard-109.png)} -.card_110{background-image:url(cards100/WWcard-110.png)} - +#war_ends.year_1779 { background-image:url(cards75/card_7_rot.png); } +#war_ends.year_1780 { background-image:url(cards75/card_8_rot.png); } +#war_ends.year_1781 { background-image:url(cards75/card_9_rot.png); } +#war_ends.year_1782 { background-image:url(cards75/card_10_rot.png); } +#war_ends.year_1783 { background-image:url(cards75/card_11_rot.png); } + +.reinforcements.ops_3 { background-image:url(cards75/card_2.png); } +.reinforcements.ops_2 { background-image:url(cards75/card_3.png); } +.reinforcements.ops_1 { background-image:url(cards75/card_4.png); } + +.card_0{background-image:url(cards100/card_1.png)} +.card_1{background-image:url(cards100/card_2.png)} +.card_2{background-image:url(cards100/card_2.png)} +.card_3{background-image:url(cards100/card_2.png)} +.card_4{background-image:url(cards100/card_2.png)} +.card_5{background-image:url(cards100/card_2.png)} +.card_6{background-image:url(cards100/card_2.png)} +.card_7{background-image:url(cards100/card_2.png)} +.card_8{background-image:url(cards100/card_2.png)} +.card_9{background-image:url(cards100/card_2.png)} +.card_10{background-image:url(cards100/card_2.png)} +.card_11{background-image:url(cards100/card_2.png)} +.card_12{background-image:url(cards100/card_2.png)} +.card_13{background-image:url(cards100/card_2.png)} +.card_14{background-image:url(cards100/card_2.png)} +.card_15{background-image:url(cards100/card_2.png)} +.card_16{background-image:url(cards100/card_2.png)} +.card_17{background-image:url(cards100/card_2.png)} +.card_18{background-image:url(cards100/card_2.png)} +.card_19{background-image:url(cards100/card_2.png)} +.card_20{background-image:url(cards100/card_2.png)} +.card_21{background-image:url(cards100/card_2.png)} +.card_22{background-image:url(cards100/card_2.png)} +.card_23{background-image:url(cards100/card_3.png)} +.card_24{background-image:url(cards100/card_3.png)} +.card_25{background-image:url(cards100/card_3.png)} +.card_26{background-image:url(cards100/card_3.png)} +.card_27{background-image:url(cards100/card_3.png)} +.card_28{background-image:url(cards100/card_3.png)} +.card_29{background-image:url(cards100/card_3.png)} +.card_30{background-image:url(cards100/card_3.png)} +.card_31{background-image:url(cards100/card_3.png)} +.card_32{background-image:url(cards100/card_3.png)} +.card_33{background-image:url(cards100/card_3.png)} +.card_34{background-image:url(cards100/card_3.png)} +.card_35{background-image:url(cards100/card_3.png)} +.card_36{background-image:url(cards100/card_3.png)} +.card_37{background-image:url(cards100/card_3.png)} +.card_38{background-image:url(cards100/card_3.png)} +.card_39{background-image:url(cards100/card_3.png)} +.card_40{background-image:url(cards100/card_3.png)} +.card_41{background-image:url(cards100/card_3.png)} +.card_42{background-image:url(cards100/card_3.png)} +.card_43{background-image:url(cards100/card_3.png)} +.card_44{background-image:url(cards100/card_3.png)} +.card_45{background-image:url(cards100/card_4.png)} +.card_46{background-image:url(cards100/card_4.png)} +.card_47{background-image:url(cards100/card_4.png)} +.card_48{background-image:url(cards100/card_4.png)} +.card_49{background-image:url(cards100/card_4.png)} +.card_50{background-image:url(cards100/card_4.png)} +.card_51{background-image:url(cards100/card_4.png)} +.card_52{background-image:url(cards100/card_4.png)} +.card_53{background-image:url(cards100/card_4.png)} +.card_54{background-image:url(cards100/card_4.png)} +.card_55{background-image:url(cards100/card_4.png)} +.card_56{background-image:url(cards100/card_4.png)} +.card_57{background-image:url(cards100/card_4.png)} +.card_58{background-image:url(cards100/card_4.png)} +.card_59{background-image:url(cards100/card_4.png)} +.card_60{background-image:url(cards100/card_4.png)} +.card_61{background-image:url(cards100/card_4.png)} +.card_62{background-image:url(cards100/card_4.png)} +.card_63{background-image:url(cards100/card_4.png)} +.card_64{background-image:url(cards100/card_4.png)} +.card_65{background-image:url(cards100/card_4.png)} +.card_66{background-image:url(cards100/card_4.png)} +.card_67{background-image:url(cards100/card_5.png)} +.card_68{background-image:url(cards100/card_5.png)} +.card_69{background-image:url(cards100/card_5.png)} +.card_70{background-image:url(cards100/card_6.png)} +.card_71{background-image:url(cards100/card_7.png)} +.card_72{background-image:url(cards100/card_8.png)} +.card_73{background-image:url(cards100/card_9.png)} +.card_74{background-image:url(cards100/card_10.png)} +.card_75{background-image:url(cards100/card_11.png)} +.card_76{background-image:url(cards100/card_12.png)} +.card_77{background-image:url(cards100/card_13.png)} +.card_78{background-image:url(cards100/card_14.png)} +.card_79{background-image:url(cards100/card_15.png)} +.card_80{background-image:url(cards100/card_16.png)} +.card_81{background-image:url(cards100/card_17.png)} +.card_82{background-image:url(cards100/card_18.png)} +.card_83{background-image:url(cards100/card_19.png)} +.card_84{background-image:url(cards100/card_20.png)} +.card_85{background-image:url(cards100/card_21.png)} +.card_86{background-image:url(cards100/card_22.png)} +.card_87{background-image:url(cards100/card_23.png)} +.card_88{background-image:url(cards100/card_24.png)} +.card_89{background-image:url(cards100/card_25.png)} +.card_90{background-image:url(cards100/card_26.png)} +.card_91{background-image:url(cards100/card_27.png)} +.card_92{background-image:url(cards100/card_28.png)} +.card_93{background-image:url(cards100/card_29.png)} +.card_94{background-image:url(cards100/card_30.png)} +.card_95{background-image:url(cards100/card_31.png)} +.card_96{background-image:url(cards100/card_32.png)} +.card_97{background-image:url(cards100/card_33.png)} +.card_98{background-image:url(cards100/card_34.png)} +.card_99{background-image:url(cards100/card_35.png)} +.card_100{background-image:url(cards100/card_36.png)} +.card_101{background-image:url(cards100/card_37.png)} +.card_102{background-image:url(cards100/card_38.png)} +.card_103{background-image:url(cards100/card_39.png)} +.card_104{background-image:url(cards100/card_40.png)} +.card_105{background-image:url(cards100/card_41.png)} +.card_106{background-image:url(cards100/card_42.png)} +.card_107{background-image:url(cards100/card_43.png)} +.card_108{background-image:url(cards100/card_44.png)} +.card_109{background-image:url(cards100/card_45.png)} +.card_110{background-image:url(cards100/card_46.png)} @@ -62,7 +62,7 @@ <div class="role_info"></div> </div> </div> - <div id="turn_info"><div id="last_played" class="card show card_back"></div></div> + <div id="turn_info"><div id="last_played" class="card shrink card_0"></div></div> <div id="log"></div> </aside> @@ -70,11 +70,8 @@ <div id="mapwrap"> <div id="map"> - <div id="pc"></div> - <div id="cu"></div> - <div id="generals"></div> - <div id="markers"></div> <div id="spaces"></div> + <div id="pieces"></div> <div id="war_ends"></div> <div id="played_british_reinforcements" class="reinforcements"></div> <div id="played_american_reinforcements_1" class="reinforcements"></div> @@ -82,118 +79,7 @@ </div> </div> -<div class="hand"> -<div id="card+1" class="card card_1"></div> -<div id="card+2" class="card card_1"></div> -<div id="card+3" class="card card_1"></div> -<div id="card+4" class="card card_1"></div> -<div id="card+5" class="card card_1"></div> -<div id="card+6" class="card card_1"></div> -<div id="card+7" class="card card_1"></div> -<div id="card+8" class="card card_1"></div> -<div id="card+9" class="card card_1"></div> -<div id="card+10" class="card card_1"></div> -<div id="card+11" class="card card_1"></div> -<div id="card+12" class="card card_1"></div> -<div id="card+13" class="card card_1"></div> -<div id="card+14" class="card card_1"></div> -<div id="card+15" class="card card_1"></div> -<div id="card+16" class="card card_1"></div> -<div id="card+17" class="card card_1"></div> -<div id="card+18" class="card card_1"></div> -<div id="card+19" class="card card_1"></div> -<div id="card+20" class="card card_1"></div> -<div id="card+21" class="card card_1"></div> -<div id="card+22" class="card card_1"></div> -<div id="card+23" class="card card_23"></div> -<div id="card+24" class="card card_23"></div> -<div id="card+25" class="card card_23"></div> -<div id="card+26" class="card card_23"></div> -<div id="card+27" class="card card_23"></div> -<div id="card+28" class="card card_23"></div> -<div id="card+29" class="card card_23"></div> -<div id="card+30" class="card card_23"></div> -<div id="card+31" class="card card_23"></div> -<div id="card+32" class="card card_23"></div> -<div id="card+33" class="card card_23"></div> -<div id="card+34" class="card card_23"></div> -<div id="card+35" class="card card_23"></div> -<div id="card+36" class="card card_23"></div> -<div id="card+37" class="card card_23"></div> -<div id="card+38" class="card card_23"></div> -<div id="card+39" class="card card_23"></div> -<div id="card+40" class="card card_23"></div> -<div id="card+41" class="card card_23"></div> -<div id="card+42" class="card card_23"></div> -<div id="card+43" class="card card_23"></div> -<div id="card+44" class="card card_23"></div> -<div id="card+45" class="card card_45"></div> -<div id="card+46" class="card card_45"></div> -<div id="card+47" class="card card_45"></div> -<div id="card+48" class="card card_45"></div> -<div id="card+49" class="card card_45"></div> -<div id="card+50" class="card card_45"></div> -<div id="card+51" class="card card_45"></div> -<div id="card+52" class="card card_45"></div> -<div id="card+53" class="card card_45"></div> -<div id="card+54" class="card card_45"></div> -<div id="card+55" class="card card_45"></div> -<div id="card+56" class="card card_45"></div> -<div id="card+57" class="card card_45"></div> -<div id="card+58" class="card card_45"></div> -<div id="card+59" class="card card_45"></div> -<div id="card+60" class="card card_45"></div> -<div id="card+61" class="card card_45"></div> -<div id="card+62" class="card card_45"></div> -<div id="card+63" class="card card_45"></div> -<div id="card+64" class="card card_45"></div> -<div id="card+65" class="card card_45"></div> -<div id="card+66" class="card card_45"></div> -<div id="card+67" class="card card_67"></div> -<div id="card+68" class="card card_68"></div> -<div id="card+69" class="card card_69"></div> -<div id="card+70" class="card card_70"></div> -<div id="card+71" class="card card_71"></div> -<div id="card+72" class="card card_72"></div> -<div id="card+73" class="card card_73"></div> -<div id="card+74" class="card card_74"></div> -<div id="card+75" class="card card_75"></div> -<div id="card+76" class="card card_76"></div> -<div id="card+77" class="card card_77"></div> -<div id="card+78" class="card card_78"></div> -<div id="card+79" class="card card_79"></div> -<div id="card+80" class="card card_80"></div> -<div id="card+81" class="card card_81"></div> -<div id="card+82" class="card card_82"></div> -<div id="card+83" class="card card_83"></div> -<div id="card+84" class="card card_84"></div> -<div id="card+85" class="card card_85"></div> -<div id="card+86" class="card card_86"></div> -<div id="card+87" class="card card_87"></div> -<div id="card+88" class="card card_88"></div> -<div id="card+89" class="card card_89"></div> -<div id="card+90" class="card card_90"></div> -<div id="card+91" class="card card_91"></div> -<div id="card+92" class="card card_92"></div> -<div id="card+93" class="card card_93"></div> -<div id="card+94" class="card card_94"></div> -<div id="card+95" class="card card_95"></div> -<div id="card+96" class="card card_96"></div> -<div id="card+97" class="card card_97"></div> -<div id="card+98" class="card card_98"></div> -<div id="card+99" class="card card_99"></div> -<div id="card+100" class="card card_100"></div> -<div id="card+101" class="card card_101"></div> -<div id="card+102" class="card card_102"></div> -<div id="card+103" class="card card_103"></div> -<div id="card+104" class="card card_104"></div> -<div id="card+105" class="card card_105"></div> -<div id="card+106" class="card card_106"></div> -<div id="card+107" class="card card_107"></div> -<div id="card+108" class="card card_108"></div> -<div id="card+109" class="card card_109"></div> -<div id="card+110" class="card card_110"></div> -</div> +<div id="hand"></div> </main> @@ -1,5 +1,45 @@ "use strict" +/* + global view, data, player, send_action, action_button, scroll_with_middle_mouse +*/ + +/* COMMON */ + +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 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 +} + +/* DATA */ + const PC_NONE = 0 const PC_BRITISH = 1 const PC_AMERICAN = 2 @@ -14,11 +54,10 @@ const CU_FRENCH_MASK = 7 << CU_FRENCH_SHIFT const GENERALS = data.generals const CARDS = data.cards const SPACES = data.spaces -const COLONIES = data.colony_spaces -const BOXES = data.BOXES +const COLONIES = data.colonies +const BOXES = data.layout -const CONTINENTAL_CONGRESS_DISPERSED = data.space_index["Continental Congress Dispersed"] -const BLOCKADE_ZONES = [ "Sea1", "Sea2", "Sea3", "Sea4", "Sea5", "Sea6", "Sea7" ] +const BLOCKADE_ZONES = [ 0, 1, 2, 3, 4, 5, 6 ] const P_BRITAIN = "Britain" const P_AMERICA = "America" @@ -29,35 +68,482 @@ const F_MUTINIES = 16 const space_count = 66 const general_count = data.generals.length +const CAPTURED_GENERALS = data.space_index["Captured Generals"] +const CONTINENTAL_CONGRESS_DISPERSED = data.space_index["Continental Congress Dispersed"] +const BRITISH_REINFORCEMENTS = data.space_index["British Reinforcement Box"] +const AMERICAN_REINFORCEMENTS = data.space_index["American Leader Reinforcements"] +const FRENCH_REINFORCEMENTS = data.space_index["French Reinforcements"] +const NOWHERE = data.spaces.length + +function is_british_general(g) { + return g >= 8 +} + +/* ACCESSORS */ + +function get_space_pc(space) { + return view.cupc[space] & 3 +} + +function has_british_pc(space) { + return get_space_pc(space) === PC_BRITISH +} + +function has_american_pc(space) { + return get_space_pc(space) === PC_AMERICAN +} + +function count_british_cu(s) { + return (view.cupc[s] & CU_BRITISH_MASK) >>> CU_BRITISH_SHIFT +} + +function count_american_cu(s) { + return (view.cupc[s] & CU_AMERICAN_MASK) >>> CU_AMERICAN_SHIFT +} + +function count_french_cu(s) { + return (view.cupc[s] & CU_FRENCH_MASK) >>> CU_FRENCH_SHIFT +} + +/* BUILD UI */ + let ui = { - cards: {}, + favicon: document.getElementById("favicon"), + header: document.querySelector("header"), + status: document.getElementById("status"), + + last_played: document.getElementById("last_played"), + hand: document.getElementById("hand"), + spaces_element: document.getElementById("spaces"), + pieces_element: document.getElementById("pieces"), + + cards: [], spaces: [], + seas: [], + a_pc: [], + b_pc: [], + a_colony: [], + b_colony: [], + + a_cu: [], + b_cu: [], + f_cu: [], + generals: [], - pc: [], - cu: [], - control: [], +} - blockade: {}, +function create(t, p, ...c) { + let e = document.createElement(t) + Object.assign(e, p) + e.append(c) + return e } -function on_focus_card_tip(card_number) { - document.getElementById("tooltip").className = "card show card_" + card_number +let action_register = [] + +function register_action(target, action, id) { + target.my_action = action + target.my_id = id + target.onmousedown = on_click_action + action_register.push(target) } -function on_blur_card_tip() { - document.getElementById("tooltip").classList = "card" +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 on_focus_last_card() { - if (typeof view.last_card === 'number') { - document.getElementById("tooltip").className = "card show card_" + view.last_card +function on_click_action(evt) { + if (evt.button === 0) + send_action(evt.target.my_action, evt.target.my_id) +} + +const SPACE_W = { fortified_port: 68, winter_quarters: 78, regular: 80, box: 100 } + +function build_marker(cn, x, y, w, h) { + let e = document.createElement("div") + e.className = cn + e.style.top = Math.round(y - h/2) + "px" + e.style.left = Math.round(x - w/2) + "px" + return e +} + +function build_piece(cn, w, h) { + let e = document.createElement("div") + e.className = cn + e.my_dx = w >> 1 + e.my_dy = h >> 1 + return e +} + +function on_init() { + for (let c = 0; c <= 110; ++c) { + let e = ui.cards[c] = document.createElement("div") + e.className = "card card_" + c + if (c > 0) { + e.my_id = c + e.onclick = on_click_card + } + } + + for (let s = 0; s < data.spaces.length; ++s) { + let info = data.spaces[s] + + let e = ui.spaces[s] = document.createElement("div") + let x = info.x + let y = info.y + let w = SPACE_W[info.type] + let h = SPACE_W[info.type] + + if (info.type === "box") { + x = data.layout.box[info.name][0] + y = data.layout.box[info.name][1] + w = data.layout.box[info.name][2] + h = data.layout.box[info.name][3] + } + + e.className = "space " + info.type + " " + data.colony_name[info.colony] + e.style.left = x - w/2 + "px" + e.style.top = y - h/2 + "px" + e.style.width = w - 10 + "px" + e.style.height = h - 10 + "px" + + register_action(e, "space", s) + + ui.spaces_element.appendChild(e) + + ui.b_pc[s] = build_marker("marker pc british", x, y, 58, 66) + ui.a_pc[s] = build_marker("marker pc american", x, y, 58, 66) + + if (info.type === "box") { + x += w/2 + y += h - 50 + ui.b_cu[s] = build_marker("marker cu british", x, y, 60+2, 60+2) + ui.a_cu[s] = build_marker("marker cu american", x, y, 60+2, 60+2) + ui.f_cu[s] = build_marker("marker cu french", x, y, 60+2, 60+2) + } else { + ui.b_cu[s] = build_marker("marker cu british", x-20, y+15, 60+2, 60+2) + ui.a_cu[s] = build_marker("marker cu american", x-20, y+15, 60+2, 60+2) + ui.f_cu[s] = build_marker("marker cu french", x+10, y+15, 60+2, 60+2) + } } + + ui.b_mcu = build_piece("marker cu british", 60+2, 60+2) + ui.a_mcu = build_piece("marker cu american", 60+2, 60+2) + ui.f_mcu = build_piece("marker cu french", 60+2, 60+2) + + for (let s = 0; s < 7; ++s) { + let e = ui.seas[s] = document.createElement("div") + let [ x, y, w, h ] = data.layout.sea[s] + e.className = "space sea" + e.style.left = x + "px" + e.style.top = y + "px" + e.style.width = w - 14 + "px" + e.style.height = h - 14 + "px" + + register_action(e, "sea", s) + + ui.spaces_element.appendChild(e) + } + + for (let g = 0; g < general_count; ++g) { + let e = ui.generals[g] = build_piece("marker general small " + data.generals[g].name, 45+2, 45+2) + register_action(e, "general", g) + } + + for (let i = 0; i < 14; ++i) { + let [ x, y ] = data.layout.colony[i] + ui.a_colony[i] = build_marker("marker small control american", x, y, 45+2, 45+2) + ui.b_colony[i] = build_marker("marker small control british", x, y, 45+2, 45+2) + } + + ui.turn_marker = build_piece("marker large turn", 55+2, 55+2) + ui.french_alliance = build_piece("marker large french_alliance", 55+2, 55+2) } -function on_blur_last_card() { - document.getElementById("tooltip").classList = "card" +on_init() + +/* UPDATE UI */ + +function show_marker(e) { + ui.pieces_element.appendChild(e) +} + +function toggle_marker(e, cond) { + if (cond) + show_marker(e) +} + +function toggle_marker_with_number(e, n) { + if (n > 0) { + show_marker(e) + e.textContent = n + } else { + e.textContent = 0 + } +} + +function show_marker_at(e, x, y) { + show_marker(e) + e.style.left = x - e.my_dx + "px" + e.style.top = y - e.my_dy + "px" +} + +function show_marker_at_xy(e, xy) { + show_marker_at(e, xy[0], xy[1]) +} + +function toggle_marker_with_number_at(e, n, x, y) { + if (n > 0) { + show_marker(e) + e.textContent = n + e.style.left = x - e.my_dx + "px" + e.style.top = y - e.my_dy + "px" + } +} + +function player_info(player, nc, nq) { + let info = "" + if (player == P_AMERICA) { + if ((view.flags & F_MUTINIES) || view.congress === CONTINENTAL_CONGRESS_DISPERSED) + info += "\u{1f6ab} " + } + if (nq > 0) + info += nq + "\u{231b} " + info += nc + "\u{1f3b4}" + return info +} + +function general_offset(g) { + let n = 0 + for (let i = 0; i < g; ++i) { + if (view.move && view.move.who === i) + continue + if (view.loca[i] === view.loca[g]) + ++n + } + return n } +function general_total(g) { + let n = 0 + for (let i = 0; i < general_count; ++i) { + if (view.move && view.move.who === i) + continue + if (view.loca[i] === view.loca[g]) + ++n + } + return n +} + +function on_update() { + let e + + roles.America.stat.textContent = player_info(P_AMERICA, view.a_cards, view.a_queue) + roles.Britain.stat.textContent = player_info(P_BRITAIN, view.b_cards, view.b_queue) + + ui.last_played.className = "card shrink card_" + view.last_played + + ui.pieces_element.replaceChildren() + + for (let s = 0; s < data.spaces.length; ++s) { + toggle_marker(ui.b_pc[s], has_british_pc(s)) + toggle_marker(ui.a_pc[s], has_american_pc(s)) + + if (view.move && view.move.to === s) { + toggle_marker_with_number(ui.a_cu[s], count_american_cu(s) - view.move.carry_american) + toggle_marker_with_number(ui.f_cu[s], count_french_cu(s) - view.move.carry_french) + toggle_marker_with_number(ui.b_cu[s], count_british_cu(s) - view.move.carry_british) + } else { + toggle_marker_with_number(ui.a_cu[s], count_american_cu(s)) + toggle_marker_with_number(ui.f_cu[s], count_french_cu(s)) + toggle_marker_with_number(ui.b_cu[s], count_british_cu(s)) + } + } + + if (view.move) { + let s = view.move.to + let x = data.spaces[s].x + let y = data.spaces[s].y + if (s >= 66) + y -= 40 + + if (view.move.carry_american > 0 && view.move.carry_french > 0) { + toggle_marker_with_number_at(ui.a_mcu, view.move.carry_american, x-15, y - 40) + toggle_marker_with_number_at(ui.f_mcu, view.move.carry_french, x+15, y - 40) + } else { + toggle_marker_with_number_at(ui.a_mcu, view.move.carry_american, x, y - 40) + toggle_marker_with_number_at(ui.f_mcu, view.move.carry_french, x, y - 40) + } + toggle_marker_with_number_at(ui.b_mcu, view.move.carry_british, x, y - 40) + } + + for (let g = 0; g < general_count; ++g) { + let s = view.loca[g] + if (s === NOWHERE) + continue + + let { x, y } = data.spaces[s] + + if (s !== FRENCH_REINFORCEMENTS && count_french_cu(s) > 0) + x += 30 + + if (view.move && view.move.who === g) { + ui.generals[g].classList.add("selected") + if (s >= 66) + y -= 40 + y -= 40 + x += 30 + } else { + ui.generals[g].classList.remove("selected") + if (s < 66) { + let offset = general_offset(g) + x += offset * (45 + 9) + y += 15 + } else { + let offset = -(general_total(g) - 1) / 2 + general_offset(g) + x += offset * (45 + 9) + y -= 15 + } + } + + show_marker_at(ui.generals[g], x, y) + } + + for (let i = 0; i < 14; ++i) { + let control = 0 + for (let s of data.colonies[i]) { + if (has_british_pc(s)) + --control + else if (has_american_pc(s)) + ++control + } + toggle_marker(ui.b_colony[i], control < 0) + toggle_marker(ui.a_colony[i], control > 0) + } + + ui.turn_marker.classList.toggle("no_regulars", !(view.flags & F_REGULARS)) + ui.french_alliance.classList.toggle("european_war", !!(view.flags & F_EUROPEAN_WAR)) + + show_marker_at_xy(ui.turn_marker, data.layout.turn[view.year]) + show_marker_at_xy(ui.french_alliance, data.layout.french_alliance_track[view.french_alliance]) + + ui.hand.replaceChildren() + if (view.hand) + for (let c of view.hand) + ui.hand.appendChild(ui.cards[c]) + + e = document.getElementById("war_ends") + if (view.war_ends) + e.className = "year_" + CARDS[view.war_ends].year + else + e.className = "" + + e = document.getElementById("played_british_reinforcements") + if (view.reinforcements[0] > 0) + e.className = "ops_" + CARDS[view.reinforcements[0]].count + else + e.className = "" + + e = document.getElementById("played_american_reinforcements_1") + if (view.reinforcements[1] > 0) + e.className = "ops_" + CARDS[view.reinforcements[1]].count + else + e.className = "" + + e = document.getElementById("played_american_reinforcements_2") + if (view.reinforcements[2] > 0) + e.className = "ops_" + CARDS[view.reinforcements[2]].count + else + e.className = "" + + for (let e of action_register) + e.classList.toggle("action", is_action(e.my_action, e.my_id)) + + action_button("pickup_french_cu", "Take French CU") + action_button("pickup_british_cu", "Take CU") + action_button("pickup_american_cu", "Take CU") + action_button("drop_french_cu", "Drop French CU") + action_button("drop_british_cu", "Drop CU") + action_button("drop_american_cu", "Drop CU") + + action_button("britain_first", "Britain") + action_button("america_first", "America") + action_button("surrender", "Surrender") + action_button("next", "Next") + action_button("done", "Done") + action_button("pass", "Pass") + action_button("undo", "Undo") +} + +/* POPUP MENU */ + +function show_popup_menu(evt, menu_id, card, title) { + let menu = document.getElementById(menu_id) + + let show = false + for (let item of menu.querySelectorAll("li")) { + let action = item.dataset.action + if (action) { + if (is_action(action, card)) { + show = true + item.classList.add("action") + item.classList.remove("disabled") + item.onclick = function () { + send_action(action, card) + hide_popup_menu() + evt.stopPropagation() + } + } else { + item.classList.remove("action") + item.classList.add("disabled") + item.onclick = null + } + } + } + + if (show) { + menu.onmouseleave = hide_popup_menu + menu.style.display = "block" + if (title) { + let item = menu.querySelector("li.title") + if (item) { + item.onclick = hide_popup_menu + item.textContent = title + } + } + + let w = menu.clientWidth + let h = menu.clientHeight + let x = Math.max(5, Math.min(evt.clientX - w / 2, window.innerWidth - w - 5)) + let y = Math.max(5, Math.min(evt.clientY - 12, window.innerHeight - h - 40)) + menu.style.left = x + "px" + menu.style.top = y + "px" + + evt.stopPropagation() + } else { + menu.style.display = "none" + } +} + +function hide_popup_menu() { + document.getElementById("popup").style.display = "none" +} + +function on_click_card(evt) { + if (view.actions) { + let c = evt.target.my_id + if (is_action("card", c)) + send_action("card", c) + else + show_popup_menu(evt, "popup", c, data.cards[c].title) + } +} + +/* LOG */ + +/* OLD --- function on_log(text) { let p = document.createElement("div") text = text.replace(/&/g, "&") @@ -83,6 +569,25 @@ function on_log(text) { return p } + +function on_focus_card_tip(card_number) { + document.getElementById("tooltip").className = "card show card_" + card_number +} + +function on_blur_card_tip() { + document.getElementById("tooltip").classList = "card" +} + +function on_focus_last_card() { + if (typeof view.last_card === 'number') { + document.getElementById("tooltip").className = "card show card_" + view.last_card + } +} + +function on_blur_last_card() { + document.getElementById("tooltip").classList = "card" +} + function clearList(container) { while (container.firstChild) container.removeChild(container.firstChild) @@ -127,7 +632,7 @@ function update_marker(e, space) { if (typeof space === "number") box = SPACES[space] else - box = BOXES[space] + return e.style.left = ((box.x - e.foo.w / 2) | 0) + "px" e.style.top = ((box.y - e.foo.h / 2) | 0) + "px" } @@ -138,82 +643,14 @@ function update_marker_xy(e, x, y) { } function build_map() { - function buildNode(type, x, y, colony) { - let e = document.createElement("div") - let w, h - e.classList.add("space") - e.classList.add(type) - if (colony && colony != "SEA") - e.classList.add("colony_" + colony) - if (type === "regular-space") { - w = 66 - h = 66 - } else if (type === "winter-quarters") { - w = 66 - h = 66 - } else if (type === "fortified-port") { - w = 54 - h = 54 - } else if (type === "blockade") { - w = 96 - h = 80 - } else { - w = 60 - h = 60 - } - let b = 7 - w -= 2 - h -= 2 - e.style.left = x - w / 2 - b + "px" - e.style.top = y - h / 2 - b + "px" - e.style.width = w + "px" - e.style.height = h + "px" - return e - } - - for (let i = 0; i < SPACES.length; ++i) { - let space = SPACES[i] - if (space.colony != null) { - let e = buildNode(space.type, space.x, space.y, space.colony) - e.my_id = i - e.addEventListener("mouseenter", onFocusNode) - e.addEventListener("mouseleave", onBlurNode) - e.addEventListener("click", on_space) - ui.spaces[i] = e - document.getElementById("spaces").appendChild(e) - ui.pc[i] = build_marker("pc", name + "-pc", space.x, space.y, 58, 66, [ "pc" ]) - } - } - - for (let zone of BLOCKADE_ZONES) { - let e = buildNode("blockade", BOXES[zone].x, BOXES[zone].y, "SEA") - e.setAttribute("id", zone) - e.addEventListener("click", on_space) - document.getElementById("spaces").appendChild(e) - ui.blockade[zone] = e - } - - ui.turn = build_marker("markers", "GameTurn", BOXES["Game Turn 1775"].x, BOXES["Game Turn 1775"].y, 55, 55, [ - "turn", - ]) - - ui.french_alliance = build_marker( - "markers", - "FrenchAlliance", - BOXES["French Alliance Track 0"].x, - BOXES["French Alliance Track 0"].y, - 55, - 55, - [ "french-alliance" ] - ) ui.french_navy = build_marker( "markers", "FrenchNavy", - SPACES[data.space_index["French Reinforcements"]].x - 130 / 2 - 10, + SPACES[data.space_index["French Reinforcements"]].x - 64 - 10, SPACES[data.space_index["French Reinforcements"]].y - 32, - 64, - 130, + //64, 130, + 55, 55, [ "french-navy" ] ) @@ -227,44 +664,11 @@ function build_map() { [ "congress" ] ) - for (let c = 0; c <= 13; ++c) { - let name = data.colony_name[c] - ui.control[c] = build_marker("markers", "control_" + name, BOXES[name].x, BOXES[name].y, 45, 45, [ "control" ]) - } - - for (let c = 1; c <= 110; ++c) { - ui.cards[c] = document.getElementById("card+" + c) - ui.cards[c].addEventListener("click", on_card) - } - - for (let g = 0; g < general_count; ++g) { - let color = GENERALS[g].owner.toLowerCase() - let name = GENERALS[g].name - //ui.generals[g] = build_marker("generals", g, 0, 0, 64, 130, [ "general", "tall", color, name, "offmap" ]) - ui.generals[g] = build_marker("generals", g, 0, 0, 45, 45, [ "general", "small", color, name, "offmap" ]) - ui.generals[g].addEventListener("click", on_general) - } -} - -function get_space_pc(space) { - return view.cupc[space] & 3 -} - -function count_british_cu(s) { - return (view.cupc[s] & CU_BRITISH_MASK) >>> CU_BRITISH_SHIFT -} - -function count_american_cu(s) { - return (view.cupc[s] & CU_AMERICAN_MASK) >>> CU_AMERICAN_SHIFT -} - -function count_french_cu(s) { - return (view.cupc[s] & CU_FRENCH_MASK) >>> CU_FRENCH_SHIFT } function update_units() { - const generalX = -22 - const generalY = 22 + const generalX = 50 + const generalY = 0 const cuX = 20 const cuY = 10 @@ -283,12 +687,9 @@ function update_units() { ui.french_alliance.classList.remove("european-war") if (view.french_navy < 0) { - let x = SPACES[data.space_index["French Reinforcements"]].x - 130 / 2 - 10 + let x = SPACES[data.space_index["French Reinforcements"]].x - 64 - 10 let y = SPACES[data.space_index["French Reinforcements"]].y - 32 - let w = 126 / 2 - let h = 252 / 2 - ui.french_navy.style.left = ((x - w / 2) | 0) + "px" - ui.french_navy.style.top = ((y - h / 2) | 0) + "px" + update_marker_xy(ui.french_navy, x, y) } else { let s if (view.french_navy > 1700) @@ -329,15 +730,23 @@ function update_units() { ui.control[c].className = "marker control" } + let count = [] + for (let g in GENERALS) { + let loc = view.loca[g] + count[loc] = (count[loc] | 0) + 1 + } + let offset = [] for (let g in GENERALS) { let e = ui.generals[g] let loc = view.loca[g] let space = SPACES[loc] || BOXES[loc] if (space) { + let n = count[loc] let o = offset[loc] | 0 - // update_marker_xy(e, space.x + o * generalX, space.y + o * generalY - 32) - update_marker_xy(e, space.x + o * generalX + 20, space.y + o * generalY - 20) + let oo = 0 + o // -(n-1)/2 + o + // update_marker_xy(e, space.x + oo * generalX, space.y + oo * generalY - 32) + update_marker_xy(e, space.x + oo * generalX + 20, space.y + oo * generalY - 10) e.classList.remove("offmap") offset[loc] = ++o } else { @@ -592,3 +1001,4 @@ function on_general(evt) { function toggle_markers() { document.querySelector("#map").classList.toggle("hide_markers") } +*/ @@ -10,7 +10,7 @@ const data = require("./data.js") const CARDS = data.cards const SPACES = data.spaces -const COLONIES = data.colony_spaces +const COLONIES = data.colonies const GENERALS = data.generals const Canada = 0 @@ -76,7 +76,7 @@ const WILMINGTON_NC = data.space_index["Wilmington NC"] const CAPTURED_GENERALS = data.space_index["Captured Generals"] const CONTINENTAL_CONGRESS_DISPERSED = data.space_index["Continental Congress Dispersed"] -const BRITISH_REINFORCEMENTS = data.space_index["British Leader Reinforcements"] +const BRITISH_REINFORCEMENTS = data.space_index["British Reinforcement Box"] const AMERICAN_REINFORCEMENTS = data.space_index["American Leader Reinforcements"] const FRENCH_REINFORCEMENTS = data.space_index["French Reinforcements"] const NOWHERE = data.spaces.length @@ -339,16 +339,16 @@ function is_map_space(s) { } function is_port(where) { - return SPACES[where].port + return SPACES[where].port >= 0 } function is_non_blockaded_port(where) { let port = SPACES[where].port - return (port > 0 && port !== game.french_navy) + return (port >= 0 && port !== game.french_navy) } function is_fortified_port(where) { - return SPACES[where].type === "fortified-port" + return SPACES[where].type === "fortified_port" } function is_continental_congress_dispersed() { @@ -360,7 +360,7 @@ function is_winter_quarter_space(where) { if (set_has(SOUTH_OF_WINTER_ATTRITION_LINE, colony)) return true // south of winter attrition line let type = SPACES[where].type - if (type === "winter-quarters" || type === "fortified-port") + if (type === "winter_quarters" || type === "fortified_port") return true return false } @@ -421,9 +421,9 @@ function is_adjacent_to_british_pc(a) { for (let b of SPACES[a].adjacent) if (has_british_pc(b)) return true - if (SPACES[a].port) { + if (is_port(a)) { for (let b of all_spaces) { - if (SPACES[b].port) + if (is_port(b)) if (has_british_pc(b)) return true } @@ -971,7 +971,7 @@ function gen_remove_british_pc_from(list_of_colonies) { for (let colony of list_of_colonies) { for (let space of COLONIES[colony]) { if (has_british_pc(space) && has_no_british_cu(space)) { - gen_action("remove_pc", space) + gen_action_space(space) } } } @@ -980,7 +980,7 @@ function gen_remove_british_pc_from(list_of_colonies) { function gen_remove_american_pc() { for (let space of all_spaces) { if (has_american_pc(space) && has_no_american_unit(space)) { - gen_action("remove_pc", space) + gen_action_space(space) } } } @@ -989,7 +989,7 @@ function gen_remove_american_pc_from(list_of_colonies) { for (let colony of list_of_colonies) { for (let space of COLONIES[colony]) { if (has_american_pc(space) && has_no_american_unit(space)) { - gen_action("remove_pc", space) + gen_action_space(space) } } } @@ -998,9 +998,9 @@ function gen_remove_american_pc_from(list_of_colonies) { function gen_remove_american_pc_from_non_port(list_of_colonies) { for (let colony of list_of_colonies) { for (let space of COLONIES[colony]) { - if (!SPACES[space].port) { + if (!is_port(space)) { if (has_american_pc(space) && has_no_american_unit(space)) { - gen_action("remove_pc", space) + gen_action_space(space) } } } @@ -1023,13 +1023,13 @@ function gen_remove_american_pc_within_two_spaces_of_a_british_general() { } for (let space of candidates) if (has_american_pc(space) && has_no_american_unit(space)) - gen_action("remove_pc", space) + gen_action_space(space) } function gen_place_american_pc() { for (let space of all_spaces) { if (has_no_pc(space) && has_no_british_playing_piece(space)) { - gen_action("place_american_pc", space) + gen_action_space(space) } } } @@ -1038,7 +1038,7 @@ function gen_place_american_pc_in(list_of_colonies) { for (let colony of list_of_colonies) { for (let space of COLONIES[colony]) { if (has_no_pc(space) && has_no_british_playing_piece(space)) { - gen_action("place_american_pc", space) + gen_action_space(space) } } } @@ -1047,7 +1047,7 @@ function gen_place_american_pc_in(list_of_colonies) { /* SETUP PHASE */ function goto_committees_of_correspondence() { - log(".h2.american Committes of Correspondence") + log("=a Committes of Correspondence") logbr() game.active = P_AMERICA game.state = "committees_of_correspondence" @@ -1062,15 +1062,15 @@ states.committees_of_correspondence = { if (game.colonies.length > 0) gen_place_american_pc_in(game.colonies) else - gen_pass() + view.actions.next = 1 }, - place_american_pc(space) { + space(s) { push_undo() - let colony = SPACES[space].colony + let colony = SPACES[s].colony set_delete(game.colonies, colony) - place_american_pc(space) + place_american_pc(s) }, - pass() { + next() { clear_undo() delete game.colonies goto_for_the_king() @@ -1079,7 +1079,7 @@ states.committees_of_correspondence = { function goto_for_the_king() { logbr() - log(".h2.british For the King") + log("=b For the King") logbr() game.active = P_BRITAIN game.state = "for_the_king" @@ -1094,14 +1094,14 @@ states.for_the_king = { if (game.count > 0) gen_british_pc_ops() else - gen_pass() + view.actions.next = 1 }, - place_british_pc(space) { + space(s) { push_undo() - place_british_pc(space) + place_british_pc(s) --game.count }, - pass() { + next() { clear_undo() gen_british_pc_ops_end() goto_start_year() @@ -1139,7 +1139,7 @@ function automatic_victory() { function goto_start_year() { logbr() - log(".h1 Year " + game.year) + log("# Year " + game.year) logbr() // Prisoner exchange @@ -1211,7 +1211,7 @@ states.british_declare_first = { gen_pass() for (let c of CAMPAIGN_CARDS) { if (game.b_hand.includes(c)) { - gen_action("card_campaign", c) + gen_action_card("card_campaign", c) } } }, @@ -1254,9 +1254,9 @@ function goto_strategy_phase(new_active) { game.state = "strategy_phase" logbr() if (game.active === P_AMERICA) - log(".h2.american American Turn") + log("=a America") else - log(".h2.british British Turn") + log("=b Britain") logbr() } @@ -1374,38 +1374,38 @@ function gen_strategy_plays(hand) { let card = CARDS[c] switch (card.type) { case "mandatory-event": - gen_action("card_play_event", c) + gen_action_card("card_play_event", c) break case "campaign": - gen_action("card_campaign", c) + gen_action_card("card_campaign", c) break case "ops": if (can_exchange_for_discard(c)) - gen_action("exchange_for_discard", c) + gen_action_card("exchange_for_discard", c) if (can_activate_general(c)) - gen_action("card_ops_general", c) - gen_action("card_ops_pc", c) + gen_action_card("card_ops_general", c) + gen_action_card("card_ops_pc", c) if (can_play_reinforcements()) - gen_action("card_ops_reinforcements", c) + gen_action_card("card_ops_reinforcements", c) if (card.count < 3) - gen_action("card_ops_queue", c) + gen_action_card("card_ops_queue", c) break case "british-event": case "british-event-or-battle": if (game.active === P_BRITAIN) if (can_play_event(c)) - gen_action("card_play_event", c) - gen_action("card_discard_event", c) + gen_action_card("card_play_event", c) + gen_action_card("card_discard_event", c) break case "american-event": if (game.active === P_AMERICA) if (can_play_event(c)) - gen_action("card_play_event", c) - gen_action("card_discard_event", c) + gen_action_card("card_play_event", c) + gen_action_card("card_discard_event", c) break case "british-battle": case "american-battle": - gen_action("card_discard_event", c) + gen_action_card("card_discard_event", c) break } } @@ -1422,20 +1422,22 @@ states.discard_event_pc_action = { else gen_american_discard_event_pc_action() }, - place_british_pc(space) { - place_british_pc(space) - end_strategy_card() - }, - place_american_pc(space) { - place_american_pc(space) - end_strategy_card() - }, - remove_pc(space) { - remove_pc(space) - end_strategy_card() - }, - flip_pc(space) { - flip_pc(space) + space(s) { + if (game.active === P_BRITAIN) { + if (has_no_pc(s)) + place_british_pc(s) + else if (has_british_army(s)) + flip_pc(s) + else + remove_pc(s) + } else { + if (has_no_pc(s)) + place_american_pc(s) + else if (has_british_army(s)) + flip_pc(s) + else + remove_pc(s) + } end_strategy_card() }, pass() { @@ -1447,11 +1449,11 @@ function gen_british_discard_event_pc_action() { for (let space of all_spaces) { if (is_adjacent_to_british_pc(space)) { if (has_no_pc(space) && has_no_american_unit(space)) - gen_action("place_british_pc", space) + gen_action_space(space) else if (has_american_pc(space) && has_british_army(space)) - gen_action("flip_pc", space) + gen_action_space(space) else if (has_american_pc(space) && has_no_american_unit(space)) - gen_action("remove_pc", space) + gen_action_space(space) } } } @@ -1461,11 +1463,11 @@ function gen_american_discard_event_pc_action() { if (is_adjacent_to_american_pc(space)) { if (has_no_pc(space) && has_no_british_cu(space)) { if (allowed_to_place_american_pc()) - gen_action("place_american_pc", space) + gen_action_space(space) } else if (has_british_pc(space) && has_american_or_french_general(space)) { - gen_action("flip_pc", space) + gen_action_space(space) } else if (has_british_pc(space) && has_no_british_cu(space)) { - gen_action("remove_pc", space) + gen_action_space(space) } } } @@ -1491,19 +1493,19 @@ states.ops_pc = { gen_american_pc_ops() } }, - place_british_pc(space) { - push_undo() - place_british_pc(space) - --game.count - }, - place_american_pc(space) { - push_undo() - place_american_pc(space) - --game.count - }, - flip_pc(space) { + space(s) { push_undo() - flip_pc(space) + if (game.active === P_BRITAIN) { + if (has_american_pc(s)) + flip_pc(s) + else + place_british_pc(s) + } else { + if (has_british_pc(s)) + flip_pc(s) + else + place_american_pc(s) + } --game.count }, pass() { @@ -1525,13 +1527,13 @@ function gen_british_pc_ops_start() { function gen_british_pc_ops() { for (let space of game.british_pc_space_list) - gen_action("place_british_pc", space) + gen_action_space(space) for (let space of all_spaces) { if (has_british_army(space)) { if (has_no_pc(space)) - gen_action("place_british_pc", space) + gen_action_space(space) else if (has_american_pc(space)) - gen_action("flip_pc", space) + gen_action_space(space) } } } @@ -1544,9 +1546,9 @@ function gen_american_pc_ops() { for (let space of all_spaces) { if (has_no_pc(space) && has_no_british_cu(space)) { if (allowed_to_place_american_pc()) - gen_action("place_american_pc", space) + gen_action_space(space) } else if (has_british_pc(space) && has_american_or_french_general(space)) { - gen_action("flip_pc", space) + gen_action_space(space) } } } @@ -1574,6 +1576,7 @@ states.ops_british_reinforcements_who = { prompt() { view.prompt = "Reinforcements: choose an available general or pass to bring only CU." view.prompt += " Carrying " + game.count + " British CU." + view.move = { to: BRITISH_REINFORCEMENTS, who: NOBODY, carry_british: game.count } gen_pass() gen_british_reinforcements_who() }, @@ -1583,7 +1586,7 @@ states.ops_british_reinforcements_who = { pickup_british_cu() { ++game.count }, - select_general(g) { + general(g) { push_undo() game.state = "ops_british_reinforcements_where" game.who = g @@ -1599,6 +1602,7 @@ states.ops_british_reinforcements_where = { prompt() { view.prompt = "Reinforcements: choose a port space." view.prompt += " Carrying " + game.count + " British CU." + view.move = { to: BRITISH_REINFORCEMENTS, who: game.who, carry_british: game.count } gen_british_reinforcements_where() }, drop_british_cu() { @@ -1607,7 +1611,7 @@ states.ops_british_reinforcements_where = { pickup_british_cu() { ++game.count }, - place_reinforcements(space) { + space(space) { place_british_reinforcements(game.who, game.count, space) end_strategy_card() delete game.who @@ -1617,10 +1621,11 @@ states.ops_british_reinforcements_where = { states.ops_american_reinforcements_who = { prompt() { view.prompt = "Reinforcements: choose an available general or pass to bring only CU." + view.move = { to: AMERICAN_REINFORCEMENTS, who: NOBODY, carry_american: game.count } gen_pass() gen_american_reinforcements_who() }, - select_general(g) { + general(g) { push_undo() game.state = "ops_american_reinforcements_where" game.who = g @@ -1635,9 +1640,10 @@ states.ops_american_reinforcements_who = { states.ops_american_reinforcements_where = { prompt() { view.prompt = "Reinforcements: choose a space." + view.move = { to: AMERICAN_REINFORCEMENTS, who: game.who, carry_american: game.count } gen_american_reinforcements_where(game.who) }, - place_reinforcements(space) { + space(space) { if (game.who === ROCHAMBEAU) place_french_reinforcements(game.who, space) else @@ -1650,7 +1656,7 @@ states.ops_american_reinforcements_where = { function gen_british_reinforcements_who() { for (let g of BRITISH_GENERALS) { if (is_general_at_location(g, BRITISH_REINFORCEMENTS)) { - gen_action("select_general", g) + gen_action_general(g) } } if (game.count > 0) @@ -1663,7 +1669,7 @@ function gen_british_reinforcements_where() { for (let space of all_spaces) { if (is_non_blockaded_port(space)) if (!has_american_or_french_cu(space) && !has_american_pc(space)) - gen_action("place_reinforcements", space) + gen_action_space(space) } if (game.count > 0) gen_action("drop_british_cu") @@ -1674,7 +1680,7 @@ function gen_british_reinforcements_where() { function gen_american_reinforcements_who() { for (let g of AMERICAN_GENERALS) { if (is_general_at_location(g, AMERICAN_REINFORCEMENTS)) { - gen_action("select_general", g) + gen_action_general(g) } } } @@ -1683,10 +1689,10 @@ function gen_american_reinforcements_where(general) { for (let space of all_spaces) { if (!has_british_cu(space) && !has_british_pc(space)) { if (general === ROCHAMBEAU) { - if (SPACES[space].port) - gen_action("place_reinforcements", space) + if (is_port(space)) + gen_action_space(space) } else { - gen_action("place_reinforcements", space) + gen_action_space(space) } } } @@ -1729,7 +1735,7 @@ states.ops_general_who = { flip_pc(where) end_strategy_card() }, - select_general(g) { + general(g) { push_undo() goto_ops_general_move(g, false) }, @@ -1761,13 +1767,13 @@ function gen_activate_general() { function gen_activate_british_general() { for (let g of BRITISH_GENERALS) if (is_general_on_map(g) && GENERALS[g].strategy <= game.count && !has_general_moved(g)) - gen_action("select_general", g) + gen_action_general(g) } function gen_activate_american_general() { for (let g of AMERICAN_GENERALS) if (is_general_on_map(g) && GENERALS[g].strategy <= game.count && !has_general_moved(g)) - gen_action("select_general", g) + gen_action_general(g) } function goto_remove_general(where) { @@ -1778,9 +1784,9 @@ function goto_remove_general(where) { states.remove_general = { prompt() { view.prompt = "Remove a general to the reinforcements box." - gen_remove_general() + gen_remove_general(game.where) }, - select_general(g) { + general(g) { if (game.active === P_BRITAIN) move_general(g, BRITISH_REINFORCEMENTS) else @@ -1796,9 +1802,9 @@ function goto_remove_general_after_intercept() { states.remove_general_after_intercept = { prompt() { view.prompt = "Remove a general to the reinforcements box." - gen_remove_general() + gen_remove_general(game.move.to) }, - select_general(g) { + general(g) { if (game.active === P_BRITAIN) move_general(g, BRITISH_REINFORCEMENTS) else @@ -1815,9 +1821,9 @@ function goto_remove_general_after_retreat(where) { states.remove_general_after_retreat = { prompt() { view.prompt = "Remove a general to the reinforcements box." - gen_remove_general() + gen_remove_general(game.where) }, - select_general(g) { + general(g) { if (game.active === P_BRITAIN) move_general(g, BRITISH_REINFORCEMENTS) else @@ -1826,24 +1832,24 @@ states.remove_general_after_retreat = { }, } -function gen_remove_general() { +function gen_remove_general(where) { if (game.active === P_BRITAIN) - return gen_remove_british_general() + return gen_remove_british_general(where) else - return gen_remove_american_general() + return gen_remove_american_general(where) } -function gen_remove_british_general() { +function gen_remove_british_general(where) { for (let g of BRITISH_GENERALS) - if (is_general_at_location(g, game.where)) - gen_action("select_general", g) + if (is_general_at_location(g, where)) + gen_action_general(g) } -function gen_remove_american_general() { +function gen_remove_american_general(where) { for (let g of AMERICAN_GENERALS) if (g !== WASHINGTON) - if (is_general_at_location(g, game.where)) - gen_action("select_general", g) + if (is_general_at_location(g, where)) + gen_action_general(g) } function goto_ops_general_move(g, marblehead) { @@ -1942,7 +1948,7 @@ states.ops_general_move = { mark_moved_french_cu(location_of_general(game.move.who), 1) }, - move(to) { + space(to) { push_undo() set_general_moved(game.move.who) @@ -2011,7 +2017,7 @@ function gen_intercept() { if (has_american_army(space)) { let g = find_american_or_french_general(space) if (g && !has_general_moved(g)) - gen_action("select_general", g) + gen_action_general(g) } } } @@ -2028,7 +2034,7 @@ states.intercept = { gen_pass() gen_intercept() }, - select_general(g) { + general(g) { set_general_moved(g) let die = roll_d6() if (die <= GENERALS[g].agility) { @@ -2145,10 +2151,10 @@ function gen_move_general() { if (game.move.mobility && has_enemy_cu(to)) { if (game.move.count - mp >= 1) - gen_action("move", to) + gen_action_space(to) } else { if (game.move.count - mp >= 0) - gen_action("move", to) + gen_action_space(to) } } if (game.active === P_BRITAIN && game.count === 4) { @@ -2161,7 +2167,7 @@ function gen_move_general() { if (alone && has_enemy_general(to)) continue // TODO: duplicate action if can move by normal road - gen_action("move", to) + gen_action_space(to) } } } @@ -2271,7 +2277,7 @@ states.remove_british_pc_from = { gen_pass() gen_remove_british_pc_from(game.where) }, - remove_pc(where) { + space(where) { remove_pc(where) if (--game.count === 0) { delete game.where @@ -2296,7 +2302,7 @@ states.remove_american_pc = { gen_pass() gen_remove_american_pc() }, - remove_pc(where) { + space(where) { remove_pc(where) if (--game.count === 0) { end_strategy_card() @@ -2320,7 +2326,7 @@ states.remove_american_pc_from = { gen_pass() gen_remove_american_pc_from(game.where) }, - remove_pc(where) { + space(where) { remove_pc(where) if (--game.count === 0) { delete game.where @@ -2347,7 +2353,7 @@ states.remove_american_pc_from_non_port = { gen_pass() gen_remove_american_pc_from_non_port(game.where) }, - remove_pc(where) { + space(where) { remove_pc(where) if (--game.count === 0) { delete game.where @@ -2372,7 +2378,7 @@ states.remove_american_pc_within_two_spaces_of_a_british_general = { gen_pass() gen_remove_american_pc_within_two_spaces_of_a_british_general() }, - remove_pc(where) { + space(where) { remove_pc(where) if (--game.count === 0) { delete game.where @@ -2447,16 +2453,17 @@ states.lord_sandwich_coastal_raids = { gen_pass() gen_lord_sandwich_coastal_raids(game.where) }, - place_british_pc(where) { - place_british_pc(where) + space(where) { + if (has_american_pc(space)) { + game.where = where + remove_pc(where) + if (--game.count === 0) + end_strategy_card() + } else { + place_british_pc(where) + } end_strategy_card() }, - remove_pc(where) { - game.where = where - remove_pc(where) - if (--game.count === 0) - end_strategy_card() - }, pass() { end_strategy_card() }, @@ -2464,11 +2471,11 @@ states.lord_sandwich_coastal_raids = { function gen_lord_sandwich_coastal_raids(first_removed) { for (let space of all_spaces) { - if (SPACES[space].port) + if (is_port(space)) if (has_american_pc(space) && has_no_american_unit(space)) - gen_action("remove_pc", space) + gen_action_space(space) if (space === first_removed) - gen_action("place_british_pc", space) + gen_action_space(space) } } @@ -2578,7 +2585,7 @@ states.john_glovers_marblehead_regiment_who = { view.prompt = "Activate an American general." gen_activate_general() }, - select_general(g) { + general(g) { goto_ops_general_move(g, true) }, } @@ -2638,7 +2645,7 @@ states.george_washington_captured = { gen_pass() gen_remove_american_pc() }, - remove_pc(where) { + space(where) { remove_pc(where) if (--game.count === 0) { end_george_washington_captured() @@ -2706,7 +2713,7 @@ states.retreat_before_battle = { gen_pass() gen_defender_retreat() }, - move(to) { + space(to) { let who = find_american_or_french_general(game.move.to) let agility = GENERALS[who].agility if (GENERALS[who].bonus) @@ -2739,9 +2746,9 @@ function goto_remove_general_after_retreat_before_battle(where) { states.remove_general_after_retreat_before_battle = { prompt() { view.prompt = "Remove a general to the reinforcements box." - gen_remove_general() + gen_remove_general(game.where) }, - select_general(g) { + general(g) { if (game.active === P_BRITAIN) move_general(g, BRITISH_REINFORCEMENTS) else @@ -2786,7 +2793,7 @@ function gen_defender_retreat() { let from = game.move.to for (let to of SPACES[from].adjacent) { if (can_defender_retreat(to)) - gen_action("move", to) + gen_action_space(to) } if (game.active === P_BRITAIN) { let can_sea_retreat = false @@ -2802,7 +2809,7 @@ function gen_defender_retreat() { for (let to of all_spaces) { if (to !== from && is_non_blockaded_port(to)) { if (!has_american_pc(to) && !has_american_or_french_cu(to)) { - gen_action("move", to) + gen_action_space(to) } } } @@ -2812,7 +2819,7 @@ function gen_defender_retreat() { function gen_attacker_retreat() { if (can_attacker_retreat()) - gen_action("move", game.move.from) + gen_action_space(game.move.from) } function end_retreat_before_battle() { @@ -2906,12 +2913,12 @@ function gen_battle_card() { switch (card.type) { case "british-battle": case "british-event-or-battle": - gen_action("card_battle_play", c) + gen_action_card("card_battle_play", c) break case "british-event": case "american-event": case "american-battle": - gen_action("card_battle_discard", c) + gen_action_card("card_battle_discard", c) break } } else { @@ -2920,10 +2927,10 @@ function gen_battle_card() { case "british-event-or-battle": case "british-event": case "american-event": - gen_action("card_battle_discard", c) + gen_action_card("card_battle_discard", c) break case "american-battle": - gen_action("card_battle_play", c) + gen_action_card("card_battle_play", c) break } } @@ -2952,7 +2959,7 @@ function roll_loser_combat_losses(log) { } function roll_winner_combat_losses(log, losing_general) { - let agility = losing_general ? GENERALS[losing_general].agility : 0 + let agility = losing_general !== NOBODY ? GENERALS[losing_general].agility : 0 let roll = roll_d6() log.push("Enemy Agility Rating: " + agility) log.push("Winner Combat Loss Roll: " + roll) @@ -3011,12 +3018,14 @@ function resolve_battle() { let b_br = 0 let a_br = 0 - if (b_g) + if (b_g !== NOBODY) b_log.push("General: " + b_g) - if (a_g) + if (a_g !== NOBODY) a_log.push("General: " + a_g) - if (b_g) { +console.log("GENS", a_g, b_g) + + if (b_g !== NOBODY) { let roll = roll_d6() if (roll <= 3) b_br = Math.min(Math.floor(GENERALS[b_g].battle / 2), b_cu) @@ -3025,7 +3034,7 @@ function resolve_battle() { b_log.push("Actual Battle Rating Roll: " + roll) } - if (a_g) { + if (a_g !== NOBODY) { let roll = roll_d6() if (roll <= 3) a_br = Math.min(Math.floor(GENERALS[a_g].battle / 2), a_cu) @@ -3153,7 +3162,7 @@ states.retreat_after_battle = { else gen_defender_retreat() }, - move(to) { + space(to) { logp("retreated to " + to) if (game.active === P_BRITAIN) retreat_british_army(game.move.to, to) @@ -3283,13 +3292,7 @@ function goto_french_naval_phase() { } function gen_place_french_navy() { - gen_action("place_navy", "Sea1") - gen_action("place_navy", "Sea2") - gen_action("place_navy", "Sea3") - gen_action("place_navy", "Sea4") - gen_action("place_navy", "Sea5") - gen_action("place_navy", "Sea6") - gen_action("place_navy", "Sea7") + view.actions.sea = [ 0, 1, 2, 3, 4, 5, 6 ] } states.place_french_navy_trigger = { @@ -3297,7 +3300,7 @@ states.place_french_navy_trigger = { view.prompt = "Place the French Navy in a blockade zone." gen_place_french_navy() }, - place_navy(zone) { + sea(zone) { logp("placed French Navy.") game.french_navy = zone goto_place_rochambeau() @@ -3329,13 +3332,13 @@ states.place_rochambeau = { for (let space in SPACES) { if (is_port(space)) { if (!has_british_cu(space) && !has_british_pc(space)) { - gen_action("place_reinforcements", space) + gen_action_space(space) can_place = true } } } }, - place_reinforcements(space) { + space(space) { logp("placed Rochambeau .") move_general(ROCHAMBEAU, space) move_cu(FRENCH, FRENCH_REINFORCEMENTS, space, 5) @@ -3354,7 +3357,7 @@ states.place_french_navy = { view.prompt = "Place the French Navy in a blockade zone." gen_place_french_navy() }, - place_navy(zone) { + sea(zone) { logp("placed French Navy.") game.french_navy = zone goto_political_control_phase() @@ -3576,17 +3579,29 @@ states.game_over = { /* CLIENT/SERVER COMMS */ function gen_action(action, argument) { - if (argument === undefined) { - view.actions[action] = 1 - } else { - if (!(action in view.actions)) - view.actions[action] = [] - set_add(view.actions[action], argument) - } + if (!(action in view.actions)) + view.actions[action] = [] + set_add(view.actions[action], argument) +} + +function gen_action_sea(s) { + gen_action("sea", s) +} + +function gen_action_space(s) { + gen_action("space", s) +} + +function gen_action_general(g) { + gen_action("general", g) +} + +function gen_action_card(action, c) { + gen_action(action, c) } function gen_pass() { - gen_action("pass") + view.actions.pass = 1 } exports.scenarios = [ "Standard" ] @@ -3624,33 +3639,35 @@ exports.view = function (state, current) { view = { prompt: null, - log: state.log, - year: state.year, - flags: state.flags, - war_ends: state.war_ends, - reinforcements: state.reinforcements, - french_alliance: state.french_alliance, - congress: state.congress, - french_navy: state.french_navy, + log: game.log, + year: game.year, + flags: game.flags, + war_ends: game.war_ends, + reinforcements: game.reinforcements, + french_alliance: game.french_alliance, + congress: game.congress, + french_navy: game.french_navy, + + loca: game.loca, + cupc: game.cupc, - loca: state.loca, - cupc: state.cupc, + a_cards: game.a_hand.length, + b_cards: game.b_hand.length, + a_queue: game.a_queue, + b_queue: game.b_queue, + last_played: game.last_played, - a_cards: state.a_hand.length, - b_cards: state.b_hand.length, - a_queue: state.a_queue, - b_queue: state.b_queue, - last_played: state.last_played, + move: game.move } if (current === P_AMERICA) - view.hand = state.a_hand + view.hand = game.a_hand else if (current === P_BRITAIN) - view.hand = state.b_hand + view.hand = game.b_hand else view.hand = [] - if (current === state.active) { + if (current === game.active) { view.actions = {} states[game.state].prompt() if (game.undo && game.undo.length > 0) diff --git a/tools/gendata.js b/tools/gendata.js new file mode 100644 index 0000000..72379ec --- /dev/null +++ b/tools/gendata.js @@ -0,0 +1,488 @@ +"use strict" + +const fs = require("fs") + +let data = JSON.parse(fs.readFileSync("tools/data.json", "utf-8")) + +function make_index_from_list(list) { + let map = {} + for (let i = 0; i < list.length; ++i) + map[list[i]] = i + return map +} + +/* GENERALS */ + +data.generals = [ + { name: "Arnold", owner: "American", strategy: 1, battle: 3, agility: 2, bonus: false }, + { name: "Gates", owner: "American", strategy: 2, battle: 2, agility: 1, bonus: false }, + { name: "Greene", owner: "American", strategy: 1, battle: 4, agility: 2, bonus: true }, + { name: "Lafayette", owner: "American", strategy: 1, battle: 2, agility: 1, bonus: false }, + { name: "Lee", owner: "American", strategy: 2, battle: 1, agility: 1, bonus: false }, + { name: "Lincoln", owner: "American", strategy: 2, battle: 1, agility: 1, bonus: false }, + { name: "Washington", owner: "American", strategy: 1, battle: 5, agility: 2, bonus: true }, + + { name: "Rochambeau", owner: "French", strategy: 2, battle: 4, agility: 2, bonus: false }, + + { name: "Burgoyne", owner: "British", strategy: 2, battle: 2, agility: 1, bonus: false }, + { name: "Carleton", owner: "British", strategy: 3, battle: 3, agility: 2, bonus: false }, + { name: "Clinton", owner: "British", strategy: 3, battle: 4, agility: 2, bonus: false }, + { name: "Cornwallis", owner: "British", strategy: 2, battle: 4, agility: 2, bonus: false }, + { name: "Howe", owner: "British", strategy: 3, battle: 6, agility: 3, bonus: false }, +] + +data.general_index = make_index_from_list(data.generals.map(x=>x.name)) + +/* COLONIES */ + +data.colony_name = [ "Canada", "NH", "NY", "MA", "CT", "RI", "PA", "NJ", "MD", "DE", "VA", "NC", "SC", "GA" ] + +const Canada = 0 +const NH = 1 +const NY = 2 +const MA = 3 +const CT = 4 +const RI = 5 +const PA = 6 +const NJ = 7 +const MD = 8 +const DE = 9 +const VA = 10 +const NC = 11 +const SC = 12 +const GA = 13 + +/* CARDS */ + +const CARD_3_OPS = { title: "3 OPS", type: "ops", count: 3 } +const CARD_2_OPS = { title: "2 OPS", type: "ops", count: 2 } +const CARD_1_OPS = { title: "1 OPS", type: "ops", count: 1 } + +data.cards = [ + null, + + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + CARD_3_OPS, + + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + CARD_2_OPS, + + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + CARD_1_OPS, + + /* 67 */ + { + title: "Minor Campaign", + type: "campaign", + count: 2, + }, + + /* 68 */ + { + title: "Minor Campaign", + type: "campaign", + count: 2, + }, + + /* 69 */ + { + title: "Minor Campaign", + type: "campaign", + count: 2, + }, + + /* 70 */ + { + title: "Major Campaign", + type: "campaign", + count: 3, + }, + + /* 71 */ + { + full_title: "North's Government Falls - The War Ends in 1779", + title: "The War Ends in 1779", + type: "mandatory-event", + event: "the_war_ends", + year: 1779, + }, + + /* 72 */ + { + full_title: "North's Government Falls - The War Ends in 1780", + title: "The War Ends in 1780", + type: "mandatory-event", + event: "the_war_ends", + year: 1780, + }, + + /* 73 */ + { + full_title: "North's Government Falls - The War Ends in 1781", + title: "The War Ends in 1781", + type: "mandatory-event", + event: "the_war_ends", + year: 1781, + }, + + /* 74 */ + { + full_title: "North's Government Falls - The War Ends in 1782", + title: "The War Ends in 1782", + type: "mandatory-event", + event: "the_war_ends", + year: 1782, + }, + + /* 75 */ + { + full_title: "North's Government Falls - The War Ends in 1783", + title: "The War Ends in 1783", + type: "mandatory-event", + event: "the_war_ends", + year: 1783, + }, + + /* 76 */ + { + title: "Henry Know Continental Artillery Commander", + type: "american-battle", + }, + + /* 77 */ + { + title: "Jane McCrea Indian Atrocity Sparks Outrage", + type: "american-event", + event: "place_american_pc", + count: 2, + once: true, + }, + + /* 78 */ + { + title: "Iroquois Uprising!", + type: "british-event", + event: "remove_american_pc_from_non_port", + where: [ NH, NY, PA ], + count: 2, + }, + + /* 79 */ + { + title: "Joseph Brant Leads an Iroquois Raid", + type: "british-event", + event: "remove_american_pc_from_non_port", + where: [ NH, NY, PA ], + count: 2, + }, + + /* 80 */ + { + title: "Lt. Colonel Simcoe's Queen's Rangers", + type: "british-event", + event: "remove_american_pc_within_two_spaces_of_a_british_general", + count: 2, + }, + + /* 81 */ + { + title: "D'Estaing Sails for the Caribbean", + type: "british-event", + when: "after_french_alliance", + event: "remove_french_navy", + }, + + /* 82 */ + { + title: "Banastre Tarleton's Waxhaws Massacre", + type: "british-event-or-battle", + event: "remove_american_cu", + }, + + /* 83 */ + { + title: "Lord George Germaine Offers Royal Amnesty", + type: "british-event", + event: "remove_american_pc", + count: 2, + }, + + /* 84 */ + { + title: "George Rogers Clark Leads a Western Offensive", + type: "american-event", + event: "remove_random_british_card", + }, + + /* 85 */ + { + title: "Don Bernardo Galvez Captures Pensacola", + type: "american-event", + when: "european_war_in_effect", + event: "remove_british_cu", + count: 2, + }, + + /* 86 */ + { + title: "Baron von Steuben Trains the Continental Army", + type: "american-event", + event: "baron_von_steuben_trains_the_continental_army", + }, + + /* 87 */ + { + title: "Lord North Offers a Royal Amnesty", + type: "british-event", + event: "remove_american_pc", + count: 4, + once: true, + }, + + /* 88 */ + { + title: "The Swamp Fox, Francis Marion", + type: "american-event", + event: "remove_british_pc_from", + where: [ NC, SC, GA ], + count: 2, + }, + + /* 89 */ + { + title: "The Gamecock: Thomas Sumter", + type: "american-event", + event: "remove_british_pc_from", + where: [ NC, SC, GA ], + count: 2, + }, + + /* 90 */ + { + title: "Josiah Martin Rallies North Carolina Loyalists", + type: "british-event", + event: "remove_american_pc_from", + where: [ NC ], + count: 2, + }, + + /* 91 */ + { + title: 'Thomas Paine Publishes Pamphlets "Common Sense" and "The American Crisis"', + type: "american-event", + event: "place_american_pc_in", + where: [ NH, NY, MA, CT, RI, PA, NJ, MD, DE, VA, NC, SC, GA ], + count: 3, + once: true, + }, + + /* 92 */ + { + title: "Nathan Hale, American Martyr", + type: "american-event", + event: "place_american_pc", + count: 2, + once: true, + }, + + /* 93 */ + { + title: "John Glover's Marblehead Regiment", + type: "american-event", + event: "john_glovers_marblehead_regiment", + }, + + /* 94 */ + { + title: "Pennsylvania and New Jersey Line Mutinies", + type: "british-event", + event: "pennsylvania_and_new_jersey_line_mutinies", + }, + + /* 95 */ + { + title: "William Pitt Urges Peace Talks", + type: "british-event", + when: "before_french_alliance", + event: "remove_american_pc", + count: 2, + reshuffle: "if_played", + }, + + /* 96 */ + { + title: "Hortelez et Cie: Clandestine French and Spanish Aid", + type: "american-event", + when: "before_french_alliance", + event: "advance_french_alliance", + reshuffle: "if_discarded", + count: 2, + }, + + /* 97 */ + { + title: "Admiral Suffren Wins a Naval Victory", + type: "american-event", + when: "after_french_alliance", + event: "remove_random_british_card", + }, + + /* 98 */ + { + title: '"Mad" Anthony Wayne', + type: "american-battle", + }, + + /* 99 */ + { + title: "Declaration of Independence", + type: "mandatory-event", + event: "declaration_of_independence", + once: true, + reshuffle: "if_played", + tournament: true, + }, + + /* 100 */ + { + title: "Benedict Arnold's Treason Undermines the Patriot Cause", + type: "british-battle", + event: "remove_benedict_arnold", + once: true, + }, + + /* 101 */ + { + title: "Benjamin Franklin: Minister to France", + type: "mandatory-event", + event: "advance_french_alliance", + count: 4, + once: true, + tournament: true, + }, + + /* 102 */ + { + title: "Admiral Rodney Captures St. Eustatius", + type: "british-event", + event: "remove_random_american_card", + when: "european_war_in_effect", + once: true, + }, + + /* 103 */ + { + title: "Thaddeus Kosciuszco Constructs Engineering Works", + type: "american-battle", + }, + + /* 104 */ + { + title: "Light Horse Harry Lee", + type: "american-battle", + }, + + /* 105 */ + { + title: "Morgan's Riflemen", + type: "american-battle", + }, + + /* 106 */ + { + title: "John Paul Jones' Shipping Raids", + type: "american-event", + event: "remove_random_british_card", + }, + + /* 107 */ + { + title: "British Light Infantry", + type: "british-battle", + }, + + /* 108 */ + { + title: "Lord Sandwich Coastal Raids", + type: "british-event", + event: "lord_sandwich_coastal_raids", + }, + + /* 109 */ + { + title: "Edward Bancroft, British Double Agent", + type: "british-event", + event: "remove_random_american_card", + }, + + /* 110 */ + { + title: "Hessian Infantry Bayonet Charge", + type: "british-battle", + }, +] + +/* OUTPUT */ + +console.log("const data = " + JSON.stringify(data,0,'\t')) +console.log("if (typeof module === 'object') module.exports = data") diff --git a/tools/layout.json b/tools/layout.json new file mode 100644 index 0000000..78b8e34 --- /dev/null +++ b/tools/layout.json @@ -0,0 +1,3728 @@ +{ + "boxes": { + "fortified_port": [ + { + "x": 450, + "y": 2052, + "w": 68, + "h": 68, + "name": "Charleston" + }, + { + "x": 852, + "y": 964, + "w": 68, + "h": 67, + "name": "Philadelphia" + }, + { + "x": 1006, + "y": 161, + "w": 68, + "h": 68, + "name": "Montreal" + }, + { + "x": 1129, + "y": 60, + "w": 67, + "h": 67, + "name": "Quebec" + } + ], + "winter_quarters": [ + { + "x": 995, + "y": 353, + "w": 79, + "h": 79, + "name": "Ticonderoga" + }, + { + "x": 1126, + "y": 339, + "w": 79, + "h": 79, + "name": "Norwich" + }, + { + "x": 878, + "y": 481, + "w": 79, + "h": 79, + "name": "Fort Stanwix" + }, + { + "x": 967, + "y": 624, + "w": 79, + "h": 79, + "name": "Albany" + }, + { + "x": 1097, + "y": 622, + "w": 79, + "h": 78, + "name": "Springfield" + }, + { + "x": 1328, + "y": 590, + "w": 79, + "h": 78, + "name": "Boston" + }, + { + "x": 1291, + "y": 733, + "w": 78, + "h": 79, + "name": "Newport" + }, + { + "x": 1185, + "y": 747, + "w": 78, + "h": 78, + "name": "Hartford" + }, + { + "x": 1025, + "y": 835, + "w": 79, + "h": 79, + "name": "New York" + }, + { + "x": 687, + "y": 745, + "w": 78, + "h": 78, + "name": "Wyoming Valley" + }, + { + "x": 441, + "y": 955, + "w": 78, + "h": 79, + "name": "Pittsburgh" + }, + { + "x": 887, + "y": 1077, + "w": 78, + "h": 79, + "name": "Wilmington DE" + }, + { + "x": 771, + "y": 1103, + "w": 79, + "h": 79, + "name": "Baltimore" + }, + { + "x": 654, + "y": 1215, + "w": 79, + "h": 78, + "name": "Alexandria" + }, + { + "x": 610, + "y": 1321, + "w": 78, + "h": 78, + "name": "Richmond" + }, + { + "x": 436, + "y": 1254, + "w": 78, + "h": 79, + "name": "Charlottesville" + }, + { + "x": 783, + "y": 1448, + "w": 78, + "h": 79, + "name": "Norfolk" + } + ], + "regular": [ + { + "x": 226.5, + "y": 2266.5, + "w": 81, + "h": 81, + "name": "Savannah" + }, + { + "x": 139.01387, + "y": 2055.9861, + "w": 81, + "h": 81, + "name": "Augusta" + }, + { + "x": 222.01385, + "y": 1927.5, + "w": 81, + "h": 81, + "name": "Ninety Six" + }, + { + "x": 403.05807, + "y": 1901.0138, + "w": 81, + "h": 81, + "name": "Eutaw Springs" + }, + { + "x": 573.5592, + "y": 1910.5598, + "w": 81, + "h": 81, + "name": "Georgetown" + }, + { + "x": 675.0592, + "y": 1811.8098, + "w": 81, + "h": 81, + "name": "Wilmington" + }, + { + "x": 495.05920000000003, + "y": 1790.0598, + "w": 81, + "h": 81, + "name": "Cheraw" + }, + { + "x": 345.3092, + "y": 1768.3098, + "w": 81, + "h": 81, + "name": "Camden" + }, + { + "x": 605.8092, + "y": 1666.5598, + "w": 81, + "h": 81, + "name": "Wake (Raleigh)" + }, + { + "x": 763.3092, + "y": 1665.5598, + "w": 81, + "h": 81, + "name": "New Bern" + }, + { + "x": 483.55920000000003, + "y": 1577.5598, + "w": 81, + "h": 81, + "name": "Hillsboro" + }, + { + "x": 308.3092, + "y": 1556.0598, + "w": 81, + "h": 81, + "name": "Salem" + }, + { + "x": 214.5592, + "y": 1648.5598, + "w": 81, + "h": 81, + "name": "Charlotte" + }, + { + "x": 122.3092, + "y": 1563.3098, + "w": 81, + "h": 81, + "name": "Gilbert Town" + }, + { + "x": 73.5592, + "y": 1768.8098, + "w": 81, + "h": 81, + "name": "Fort Prince George" + }, + { + "x": 60.260580000000004, + "y": 1446.7491, + "w": 81, + "h": 81, + "name": "Abingdon" + }, + { + "x": 211.40466, + "y": 1403.6156, + "w": 81, + "h": 81, + "name": "Fort Chiswell" + }, + { + "x": 348.58337, + "y": 1397.2516, + "w": 81, + "h": 81, + "name": "Fincastle" + }, + { + "x": 485.93884, + "y": 1439.1477, + "w": 81, + "h": 81, + "name": "Lynch's Ferry" + }, + { + "x": 634.07404, + "y": 1441.644, + "w": 81, + "h": 81, + "name": "Petersburg" + }, + { + "x": 766.12952, + "y": 1312.0383, + "w": 81, + "h": 81, + "name": "Yorktown" + }, + { + "x": 131.70384, + "y": 1213.4077, + "w": 81, + "h": 81, + "name": "Point Pleasant" + }, + { + "x": 146.95384, + "y": 979.7827, + "w": 81, + "h": 81, + "name": "Bassett Town" + }, + { + "x": 446.21503, + "y": 1093.475, + "w": 81, + "h": 81, + "name": "Fort Cumberland" + }, + { + "x": 645.7276, + "y": 1092.4473, + "w": 81, + "h": 81, + "name": "Frederick Town" + }, + { + "x": 588.75372, + "y": 987.3384000000001, + "w": 81, + "h": 81, + "name": "York" + }, + { + "x": 683.25372, + "y": 895.58838, + "w": 81, + "h": 81, + "name": "Reading" + }, + { + "x": 554.00372, + "y": 875.33838, + "w": 81, + "h": 81, + "name": "Harrisburg" + }, + { + "x": 863.60297, + "y": 826.91376, + "w": 81, + "h": 81, + "name": "Morristown" + }, + { + "x": 960.8301, + "y": 937.04565, + "w": 81, + "h": 81, + "name": "New Brunswick" + }, + { + "x": 996.8300999999999, + "y": 1069.7957, + "w": 81, + "h": 81, + "name": "Monmouth" + }, + { + "x": 1145.8301, + "y": 863.54565, + "w": 81, + "h": 81, + "name": "Long Island" + }, + { + "x": 1087.5801, + "y": 731.54565, + "w": 81, + "h": 81, + "name": "New Haven" + }, + { + "x": 954.33008, + "y": 742.29565, + "w": 81, + "h": 81, + "name": "Westchester" + }, + { + "x": 1217.8301, + "y": 623.54565, + "w": 81, + "h": 81, + "name": "Lexington Concord" + }, + { + "x": 1393.8301, + "y": 700.54565, + "w": 81, + "h": 81, + "name": "Barnstable" + }, + { + "x": 1333.5618, + "y": 405.97501, + "w": 81, + "h": 81, + "name": "Falmouth" + }, + { + "x": 1213.5618, + "y": 456.47501, + "w": 81, + "h": 81, + "name": "Concord" + }, + { + "x": 1100.8118, + "y": 481.97504000000004, + "w": 81, + "h": 81, + "name": "Brattleboro" + }, + { + "x": 991.3118, + "y": 491.22504000000004, + "w": 81, + "h": 81, + "name": "Saratoga" + }, + { + "x": 767.56177, + "y": 419.47504, + "w": 81, + "h": 81, + "name": "Oswego" + }, + { + "x": 667.56177, + "y": 577.97504, + "w": 81, + "h": 81, + "name": "Genesee" + }, + { + "x": 461.06177, + "y": 521.47504, + "w": 81, + "h": 81, + "name": "Fort Niagara" + }, + { + "x": 54.828918, + "y": 667.84613, + "w": 81, + "h": 81, + "name": "Fort Detroit" + }, + { + "x": 179.39716, + "y": 2389.0383, + "w": 81, + "h": 81, + "name": "St. Mary's" + } + ], + "sea": [ + { + "x": 351, + "y": 2352, + "w": 97, + "h": 83, + "name": "Sea7" + }, + { + "x": 629, + "y": 2022, + "w": 97, + "h": 83, + "name": "Sea6" + }, + { + "x": 938, + "y": 1389, + "w": 97, + "h": 83, + "name": "Sea5" + }, + { + "x": 1049, + "y": 1169, + "w": 97, + "h": 83, + "name": "Sea4" + }, + { + "x": 1205, + "y": 975, + "w": 97, + "h": 83, + "name": "Sea3" + }, + { + "x": 1461, + "y": 508, + "w": 97, + "h": 84, + "name": "Sea2" + }, + { + "x": 1268, + "y": 60, + "w": 97, + "h": 83, + "name": "Sea1" + } + ], + "colony": [ + { + "x": 1171, + "y": 1558, + "w": 66, + "h": 57, + "name": "GA" + }, + { + "x": 1218, + "y": 1486, + "w": 67, + "h": 58, + "name": "SC" + }, + { + "x": 1267, + "y": 1415, + "w": 66, + "h": 57, + "name": "NC" + }, + { + "x": 1248, + "y": 1344, + "w": 67, + "h": 58, + "name": "VA" + }, + { + "x": 1306, + "y": 1274, + "w": 66, + "h": 57, + "name": "MD" + }, + { + "x": 1383, + "y": 1274, + "w": 66, + "h": 57, + "name": "DE" + }, + { + "x": 1306, + "y": 1201, + "w": 66, + "h": 58, + "name": "PA" + }, + { + "x": 1383, + "y": 1196, + "w": 66, + "h": 58, + "name": "NJ" + }, + { + "x": 1462, + "y": 1163, + "w": 66, + "h": 58, + "name": "CT" + }, + { + "x": 1540, + "y": 1163, + "w": 66, + "h": 58, + "name": "RI" + }, + { + "x": 1498, + "y": 1088, + "w": 67, + "h": 58, + "name": "MA" + }, + { + "x": 1382, + "y": 1089, + "w": 66, + "h": 58, + "name": "NY" + }, + { + "x": 1465, + "y": 1014, + "w": 66, + "h": 58, + "name": "NH" + }, + { + "x": 1383, + "y": 975, + "w": 66, + "h": 57, + "name": "Canada" + } + ], + "box": [ + { + "x": 1358, + "y": 1673, + "w": 214, + "h": 206, + "name": "British Reinforcement Box" + }, + { + "x": 294, + "y": 258, + "w": 214, + "h": 206, + "name": "American Leader Reinforcements" + }, + { + "x": 63, + "y": 258, + "w": 214, + "h": 207, + "name": "French Reinforcements" + }, + { + "x": 542, + "y": 332, + "w": 88, + "h": 104, + "name": "Continental Congress Dispersed" + }, + { + "x": 1400, + "y": 46, + "w": 205, + "h": 136, + "name": "Captured Generals" + } + ], + "Card Boxes": [ + { + "x": 904, + "y": 1915, + "w": 199, + "h": 277, + "name": "BRC" + }, + { + "x": 1137, + "y": 1915, + "w": 199, + "h": 277, + "name": "ARC1" + }, + { + "x": 1370, + "y": 1915, + "w": 199.00003, + "h": 277, + "name": "ARC2" + }, + { + "x": 1027, + "y": 1679, + "w": 278, + "h": 198.99998, + "name": "War Ends Card" + } + ], + "Game Turn": [ + { + "x": 904, + "y": 2368, + "w": 64, + "h": 72, + "name": "1775" + }, + { + "x": 970, + "y": 2368, + "w": 64, + "h": 72, + "name": "1776" + }, + { + "x": 1036, + "y": 2368, + "w": 64, + "h": 72, + "name": "1777" + }, + { + "x": 1102, + "y": 2368, + "w": 64, + "h": 72, + "name": "1778" + }, + { + "x": 1168, + "y": 2368, + "w": 65, + "h": 72, + "name": "1779" + }, + { + "x": 1235, + "y": 2368, + "w": 64, + "h": 72, + "name": "1780" + }, + { + "x": 1301, + "y": 2368, + "w": 64, + "h": 72, + "name": "1781" + }, + { + "x": 1367, + "y": 2368, + "w": 64, + "h": 72, + "name": "1782" + }, + { + "x": 1433, + "y": 2368, + "w": 64, + "h": 72, + "name": "1783" + } + ], + "French Alliance Track": [ + { + "x": 905, + "y": 2241, + "w": 64, + "h": 71, + "name": "0" + }, + { + "x": 971, + "y": 2241, + "w": 64, + "h": 71, + "name": "1" + }, + { + "x": 1037, + "y": 2241, + "w": 64, + "h": 71, + "name": "2" + }, + { + "x": 1103, + "y": 2241, + "w": 64, + "h": 71, + "name": "3" + }, + { + "x": 1169, + "y": 2241, + "w": 65, + "h": 71, + "name": "4" + }, + { + "x": 1236, + "y": 2241, + "w": 64, + "h": 71, + "name": "5" + }, + { + "x": 1302, + "y": 2241, + "w": 64, + "h": 71, + "name": "6" + }, + { + "x": 1368, + "y": 2241, + "w": 64, + "h": 71, + "name": "7" + }, + { + "x": 1434, + "y": 2241, + "w": 65, + "h": 71, + "name": "8" + }, + { + "x": 1501, + "y": 2241, + "w": 63, + "h": 71, + "name": "9" + } + ] + }, + "points": { + "fortified_port": [ + { + "x": 484, + "y": 2086, + "name": "Charleston" + }, + { + "x": 886, + "y": 997.5, + "name": "Philadelphia" + }, + { + "x": 1040, + "y": 195, + "name": "Montreal" + }, + { + "x": 1162.5, + "y": 93.5, + "name": "Quebec" + } + ], + "winter_quarters": [ + { + "x": 1034.5, + "y": 392.5, + "name": "Ticonderoga" + }, + { + "x": 1165.5, + "y": 378.5, + "name": "Norwich" + }, + { + "x": 917.5, + "y": 520.5, + "name": "Fort Stanwix" + }, + { + "x": 1006.5, + "y": 663.5, + "name": "Albany" + }, + { + "x": 1136.5, + "y": 661, + "name": "Springfield" + }, + { + "x": 1367.5, + "y": 629, + "name": "Boston" + }, + { + "x": 1330, + "y": 772.5, + "name": "Newport" + }, + { + "x": 1224, + "y": 786, + "name": "Hartford" + }, + { + "x": 1064.5, + "y": 874.5, + "name": "New York" + }, + { + "x": 726, + "y": 784, + "name": "Wyoming Valley" + }, + { + "x": 480, + "y": 994.5, + "name": "Pittsburgh" + }, + { + "x": 926, + "y": 1116.5, + "name": "Wilmington DE" + }, + { + "x": 810.5, + "y": 1142.5, + "name": "Baltimore" + }, + { + "x": 693.5, + "y": 1254, + "name": "Alexandria" + }, + { + "x": 649, + "y": 1360, + "name": "Richmond" + }, + { + "x": 475, + "y": 1293.5, + "name": "Charlottesville" + }, + { + "x": 822, + "y": 1487.5, + "name": "Norfolk" + } + ], + "regular": [ + { + "x": 267, + "y": 2307, + "name": "Savannah" + }, + { + "x": 179.51387, + "y": 2096.4861, + "name": "Augusta" + }, + { + "x": 262.51385, + "y": 1968, + "name": "Ninety Six" + }, + { + "x": 443.55807, + "y": 1941.5138, + "name": "Eutaw Springs" + }, + { + "x": 614.0592, + "y": 1951.0598, + "name": "Georgetown" + }, + { + "x": 715.5592, + "y": 1852.3098, + "name": "Wilmington" + }, + { + "x": 535.5592, + "y": 1830.5598, + "name": "Cheraw" + }, + { + "x": 385.8092, + "y": 1808.8098, + "name": "Camden" + }, + { + "x": 646.3092, + "y": 1707.0598, + "name": "Wake (Raleigh)" + }, + { + "x": 803.8092, + "y": 1706.0598, + "name": "New Bern" + }, + { + "x": 524.0592, + "y": 1618.0598, + "name": "Hillsboro" + }, + { + "x": 348.8092, + "y": 1596.5598, + "name": "Salem" + }, + { + "x": 255.0592, + "y": 1689.0598, + "name": "Charlotte" + }, + { + "x": 162.8092, + "y": 1603.8098, + "name": "Gilbert Town" + }, + { + "x": 114.0592, + "y": 1809.3098, + "name": "Fort Prince George" + }, + { + "x": 100.76058, + "y": 1487.2491, + "name": "Abingdon" + }, + { + "x": 251.90466, + "y": 1444.1156, + "name": "Fort Chiswell" + }, + { + "x": 389.08337, + "y": 1437.7516, + "name": "Fincastle" + }, + { + "x": 526.43884, + "y": 1479.6477, + "name": "Lynch's Ferry" + }, + { + "x": 674.57404, + "y": 1482.144, + "name": "Petersburg" + }, + { + "x": 806.62952, + "y": 1352.5383, + "name": "Yorktown" + }, + { + "x": 172.20384, + "y": 1253.9077, + "name": "Point Pleasant" + }, + { + "x": 187.45384, + "y": 1020.2827, + "name": "Bassett Town" + }, + { + "x": 486.71503, + "y": 1133.975, + "name": "Fort Cumberland" + }, + { + "x": 686.2276, + "y": 1132.9473, + "name": "Frederick Town" + }, + { + "x": 629.25372, + "y": 1027.8384, + "name": "York" + }, + { + "x": 723.75372, + "y": 936.08838, + "name": "Reading" + }, + { + "x": 594.50372, + "y": 915.83838, + "name": "Harrisburg" + }, + { + "x": 904.10297, + "y": 867.41376, + "name": "Morristown" + }, + { + "x": 1001.3301, + "y": 977.54565, + "name": "New Brunswick" + }, + { + "x": 1037.3301, + "y": 1110.2957, + "name": "Monmouth" + }, + { + "x": 1186.3301, + "y": 904.04565, + "name": "Long Island" + }, + { + "x": 1128.0801, + "y": 772.04565, + "name": "New Haven" + }, + { + "x": 994.83008, + "y": 782.79565, + "name": "Westchester" + }, + { + "x": 1258.3301, + "y": 664.04565, + "name": "Lexington Concord" + }, + { + "x": 1434.3301, + "y": 741.04565, + "name": "Barnstable" + }, + { + "x": 1374.0618, + "y": 446.47501, + "name": "Falmouth" + }, + { + "x": 1254.0618, + "y": 496.97501, + "name": "Concord" + }, + { + "x": 1141.3118, + "y": 522.47504, + "name": "Brattleboro" + }, + { + "x": 1031.8118, + "y": 531.72504, + "name": "Saratoga" + }, + { + "x": 808.06177, + "y": 459.97504, + "name": "Oswego" + }, + { + "x": 708.06177, + "y": 618.47504, + "name": "Genesee" + }, + { + "x": 501.56177, + "y": 561.97504, + "name": "Fort Niagara" + }, + { + "x": 95.328918, + "y": 708.34613, + "name": "Fort Detroit" + }, + { + "x": 219.89716, + "y": 2429.5383, + "name": "St. Mary's" + } + ], + "sea": [ + { + "x": 399.5, + "y": 2393.5, + "name": "Sea7" + }, + { + "x": 677.5, + "y": 2063.5, + "name": "Sea6" + }, + { + "x": 986.5, + "y": 1430.5, + "name": "Sea5" + }, + { + "x": 1097.5, + "y": 1210.5, + "name": "Sea4" + }, + { + "x": 1253.5, + "y": 1016.5, + "name": "Sea3" + }, + { + "x": 1509.5, + "y": 550, + "name": "Sea2" + }, + { + "x": 1316.5, + "y": 101.5, + "name": "Sea1" + } + ], + "colony": [ + { + "x": 1204, + "y": 1586.5, + "name": "GA" + }, + { + "x": 1251.5, + "y": 1515, + "name": "SC" + }, + { + "x": 1300, + "y": 1443.5, + "name": "NC" + }, + { + "x": 1281.5, + "y": 1373, + "name": "VA" + }, + { + "x": 1339, + "y": 1302.5, + "name": "MD" + }, + { + "x": 1416, + "y": 1302.5, + "name": "DE" + }, + { + "x": 1339, + "y": 1230, + "name": "PA" + }, + { + "x": 1416, + "y": 1225, + "name": "NJ" + }, + { + "x": 1495, + "y": 1192, + "name": "CT" + }, + { + "x": 1573, + "y": 1192, + "name": "RI" + }, + { + "x": 1531.5, + "y": 1117, + "name": "MA" + }, + { + "x": 1415, + "y": 1118, + "name": "NY" + }, + { + "x": 1498, + "y": 1043, + "name": "NH" + }, + { + "x": 1416, + "y": 1003.5, + "name": "Canada" + } + ], + "box": [ + { + "x": 1465, + "y": 1776, + "name": "British Reinforcement Box" + }, + { + "x": 401, + "y": 361, + "name": "American Leader Reinforcements" + }, + { + "x": 170, + "y": 361.5, + "name": "French Reinforcements" + }, + { + "x": 586, + "y": 384, + "name": "Continental Congress Dispersed" + }, + { + "x": 1502.5, + "y": 114, + "name": "Captured Generals" + } + ], + "Card Boxes": [ + { + "x": 1003.5, + "y": 2053.5, + "name": "BRC" + }, + { + "x": 1236.5, + "y": 2053.5, + "name": "ARC1" + }, + { + "x": 1469.500015, + "y": 2053.5, + "name": "ARC2" + }, + { + "x": 1166, + "y": 1778.49999, + "name": "War Ends Card" + } + ], + "Game Turn": [ + { + "x": 936, + "y": 2404, + "name": "1775" + }, + { + "x": 1002, + "y": 2404, + "name": "1776" + }, + { + "x": 1068, + "y": 2404, + "name": "1777" + }, + { + "x": 1134, + "y": 2404, + "name": "1778" + }, + { + "x": 1200.5, + "y": 2404, + "name": "1779" + }, + { + "x": 1267, + "y": 2404, + "name": "1780" + }, + { + "x": 1333, + "y": 2404, + "name": "1781" + }, + { + "x": 1399, + "y": 2404, + "name": "1782" + }, + { + "x": 1465, + "y": 2404, + "name": "1783" + } + ], + "French Alliance Track": [ + { + "x": 937, + "y": 2276.5, + "name": "0" + }, + { + "x": 1003, + "y": 2276.5, + "name": "1" + }, + { + "x": 1069, + "y": 2276.5, + "name": "2" + }, + { + "x": 1135, + "y": 2276.5, + "name": "3" + }, + { + "x": 1201.5, + "y": 2276.5, + "name": "4" + }, + { + "x": 1268, + "y": 2276.5, + "name": "5" + }, + { + "x": 1334, + "y": 2276.5, + "name": "6" + }, + { + "x": 1400, + "y": 2276.5, + "name": "7" + }, + { + "x": 1466.5, + "y": 2276.5, + "name": "8" + }, + { + "x": 1532.5, + "y": 2276.5, + "name": "9" + } + ] + }, + "edges": { + "path": [ + { + "x1": 227.01265, + "y1": 2390.0517, + "x2": 242.96720000000002, + "y2": 2339.5508, + "name": null, + "name1": "St. Mary's", + "name2": "Savannah" + }, + { + "x1": 199.02401, + "y1": 2132.0174, + "x2": 254.46493, + "y2": 2269.0997, + "name": null, + "name1": "Augusta", + "name2": "Savannah" + }, + { + "x1": 276.94694, + "y1": 2268.2133, + "x2": 450.40404, + "y2": 2099.316, + "name": null, + "name1": "Savannah", + "name2": "Charleston" + }, + { + "x1": 201.99439, + "y1": 2062.6086, + "x2": 241.40983, + "y2": 2001.6522, + "name": null, + "name1": "Augusta", + "name2": "Ninety Six" + }, + { + "x1": 145.20426, + "y1": 1830.9903, + "x2": 237.45150999999998, + "y2": 1937.1538, + "name": null, + "name1": "Fort Prince George", + "name2": "Ninety Six" + }, + { + "x1": 284.32538, + "y1": 1934.4039, + "x2": 357.40648999999996, + "y2": 1837.6152, + "name": null, + "name1": "Ninety Six", + "name2": "Camden" + }, + { + "x1": 404.96645, + "y1": 1843.7387, + "x2": 429.42317, + "y2": 1904.1378000000002, + "name": null, + "name1": "Camden", + "name2": "Eutaw Springs" + }, + { + "x1": 460.98711, + "y1": 1978.2779, + "x2": 477.36157999999995, + "y2": 2045.6090000000002, + "name": null, + "name1": "Eutaw Springs", + "name2": "Charleston" + }, + { + "x1": 496.23604, + "y1": 2051.5256, + "x2": 576.1504, + "y2": 1963.1115, + "name": null, + "name1": "Charleston", + "name2": "Georgetown" + }, + { + "x1": 646.98186, + "y1": 1928.7791, + "x2": 689.27224, + "y2": 1883.0305, + "name": null, + "name1": "Georgetown", + "name2": "Wilmington" + }, + { + "x1": 556.73449, + "y1": 1865.4894, + "x2": 590.69173, + "y2": 1919.571, + "name": null, + "name1": "Cheraw", + "name2": "Georgetown" + }, + { + "x1": 575.69233, + "y1": 1829.9903, + "x2": 676.98091, + "y2": 1842.365, + "name": null, + "name1": "Cheraw", + "name2": "Wilmington" + }, + { + "x1": 425.11313, + "y1": 1812.7409, + "x2": 497.1111, + "y2": 1822.0322, + "name": null, + "name1": "Camden", + "name2": "Cheraw" + }, + { + "x1": 286.90869, + "y1": 1713.7435, + "x2": 358.4066, + "y2": 1780.7, + "name": null, + "name1": "Charlotte", + "name2": "Camden" + }, + { + "x1": 149.36454, + "y1": 1641.7822, + "x2": 120.95485000000001, + "y2": 1769.7419, + "name": null, + "name1": "Gilbert Town", + "name2": "Fort Prince George" + }, + { + "x1": 91.330808, + "y1": 1526.2489, + "x2": 98.12225600000001, + "y2": 1772.4086, + "name": null, + "name1": "Abingdon", + "name2": "Fort Prince George" + }, + { + "x1": 193.54031, + "y1": 1630.0024, + "x2": 227.19063, + "y2": 1659.8424, + "name": null, + "name1": "Gilbert Town", + "name2": "Charlotte" + }, + { + "x1": 278.7005, + "y1": 1656.8285, + "x2": 316.11605, + "y2": 1620.4962, + "name": null, + "name1": "Charlotte", + "name2": "Salem" + }, + { + "x1": 385.32252, + "y1": 1600.0801, + "x2": 488.73627999999997, + "y2": 1612.1214, + "name": null, + "name1": "Salem", + "name2": "Hillsboro" + }, + { + "x1": 370.08352, + "y1": 1630.7156, + "x2": 509.69412, + "y2": 1800.5745, + "name": null, + "name1": "Salem", + "name2": "Cheraw" + }, + { + "x1": 669.35138, + "y1": 1740.1481, + "x2": 704.3551299999999, + "y2": 1813.8239, + "name": null, + "name1": "Wake (Raleigh)", + "name2": "Wilmington" + }, + { + "x1": 560.0883, + "y1": 1635.6726, + "x2": 619.77668, + "y2": 1677.2911000000001, + "name": null, + "name1": "Hillsboro", + "name2": "Wake (Raleigh)" + }, + { + "x1": 686.53009, + "y1": 1705.2652, + "x2": 763.91786, + "y2": 1704.3802, + "name": null, + "name1": "Wake (Raleigh)", + "name2": "New Bern" + }, + { + "x1": 672.10609, + "y1": 1522.7905, + "x2": 654.12084, + "y2": 1667.7282, + "name": null, + "name1": "Petersburg", + "name2": "Wake (Raleigh)" + }, + { + "x1": 555.65124, + "y1": 1592.7885, + "x2": 647.69018, + "y2": 1511.7075000000002, + "name": null, + "name1": "Hillsboro", + "name2": "Petersburg" + }, + { + "x1": 714.73008, + "y1": 1482.1667, + "x2": 782.7696100000001, + "y2": 1484.375, + "name": null, + "name1": "Petersburg", + "name2": "Norfolk" + }, + { + "x1": 822.35191, + "y1": 1526.7073, + "x2": 811.0189, + "y2": 1666.6615, + "name": null, + "name1": "Norfolk", + "name2": "New Bern" + }, + { + "x1": 382.48926, + "y1": 1573.8725, + "x2": 494.40279, + "y2": 1501.6245, + "name": null, + "name1": "Salem", + "name2": "Lynch's Ferry" + }, + { + "x1": 425.69638, + "y1": 1447.0844, + "x2": 489.44458999999995, + "y2": 1465.5004999999999, + "name": null, + "name1": "Fincastle", + "name2": "Lynch's Ferry" + }, + { + "x1": 551.06786, + "y1": 1449.2093, + "x2": 610.61031, + "y2": 1393.6397, + "name": null, + "name1": "Lynch's Ferry", + "name2": "Richmond" + }, + { + "x1": 662.68958, + "y1": 1398.8774, + "x2": 669.6478999999999, + "y2": 1442.3761000000002, + "name": null, + "name1": "Richmond", + "name2": "Petersburg" + }, + { + "x1": 564.95299, + "y1": 1489.0395, + "x2": 634.49253, + "y2": 1488.5332, + "name": null, + "name1": "Lynch's Ferry", + "name2": "Petersburg" + }, + { + "x1": 687.89742, + "y1": 1388.0028, + "x2": 783.35299, + "y2": 1463.584, + "name": null, + "name1": "Richmond", + "name2": "Norfolk" + }, + { + "x1": 687.6891, + "y1": 1357.2536, + "x2": 766.5200900000001, + "y2": 1354.1287, + "name": null, + "name1": "Richmond", + "name2": "Yorktown" + }, + { + "x1": 654.48163, + "y1": 1323.1295, + "x2": 670.77284, + "y2": 1290.5471, + "name": null, + "name1": "Richmond", + "name2": "Alexandria" + }, + { + "x1": 513.48556, + "y1": 1299.7969, + "x2": 610.02451, + "y2": 1338.5457000000001, + "name": null, + "name1": "Charlottesville", + "name2": "Richmond" + }, + { + "x1": 504.31918, + "y1": 1333.0042, + "x2": 534.06834, + "y2": 1442.8345000000002, + "name": null, + "name1": "Charlottesville", + "name2": "Lynch's Ferry" + }, + { + "x1": 403.38632, + "y1": 1400.343, + "x2": 442.32085, + "y2": 1332.546, + "name": null, + "name1": "Fincastle", + "name2": "Charlottesville" + }, + { + "x1": 186.99475, + "y1": 1293.3804, + "x2": 237.99332, + "y2": 1404.5856, + "name": null, + "name1": "Point Pleasant", + "name2": "Fort Chiswell" + }, + { + "x1": 136.12124, + "y1": 1468.0004, + "x2": 211.86909, + "y2": 1448.4593, + "name": null, + "name1": "Abingdon", + "name2": "Fort Chiswell" + }, + { + "x1": 288.28357, + "y1": 1440.7096, + "x2": 351.32347, + "y2": 1440.0012, + "name": null, + "name1": "Fort Chiswell", + "name2": "Fincastle" + }, + { + "x1": 137.20438, + "y1": 1503.9995, + "x2": 165.32871999999998, + "y2": 1564.081, + "name": null, + "name1": "Abingdon", + "name2": "Gilbert Town" + }, + { + "x1": 513.91845, + "y1": 1258.4465, + "x2": 651.77343, + "y2": 1152.0509, + "name": null, + "name1": "Charlottesville", + "name2": "Frederick Town" + }, + { + "x1": 697.18887, + "y1": 1171.3421, + "x2": 699.64694, + "y2": 1214.8824, + "name": null, + "name1": "Frederick Town", + "name2": "Alexandria" + }, + { + "x1": 732.5, + "y1": 1218.25, + "x2": 773, + "y2": 1180.25, + "name": null, + "name1": "Alexandria", + "name2": "Baltimore" + }, + { + "x1": 726.27128, + "y1": 1134.3849, + "x2": 772.0616600000001, + "y2": 1136.1348, + "name": null, + "name1": "Frederick Town", + "name2": "Baltimore" + }, + { + "x1": 646.35681, + "y1": 1128.5516, + "x2": 526.5687, + "y2": 1131.7183, + "name": null, + "name1": "Frederick Town", + "name2": "Fort Cumberland" + }, + { + "x1": 479.11158, + "y1": 1033.8876, + "x2": 481.27809, + "y2": 1094.6774, + "name": null, + "name1": "Pittsburgh", + "name2": "Fort Cumberland" + }, + { + "x1": 228.78524, + "y1": 1014.3049, + "x2": 440.57096, + "y2": 999.43028, + "name": null, + "name1": "Bassett Town", + "name2": "Pittsburgh" + }, + { + "x1": 518.94388, + "y1": 974.55593, + "x2": 562.69245, + "y2": 940.5152, + "name": null, + "name1": "Pittsburgh", + "name2": "Harrisburg" + }, + { + "x1": 619.31595, + "y1": 949.01496, + "x2": 628.64909, + "y2": 988.30569, + "name": null, + "name1": "Harrisburg", + "name2": "York" + }, + { + "x1": 663.85645, + "y1": 1006.0968, + "x2": 701.35537, + "y2": 969.8478600000001, + "name": null, + "name1": "York", + "name2": "Reading" + }, + { + "x1": 759.31202, + "y1": 949.84833, + "x2": 850.2562499999999, + "y2": 985.27606, + "name": null, + "name1": "Reading", + "name2": "Philadelphia" + }, + { + "x1": 908.63011, + "y1": 1030.8842, + "x2": 920.09917, + "y2": 1078.0530999999999, + "name": null, + "name1": "Philadelphia", + "name2": "Wilmington DE" + }, + { + "x1": 849.98819, + "y1": 1137.6914, + "x2": 886.79237, + "y2": 1129.1957, + "name": null, + "name1": "Baltimore", + "name2": "Wilmington DE" + }, + { + "x1": 920.40337, + "y1": 1023.0968, + "x2": 1003.9483, + "y2": 1088.1015, + "name": null, + "name1": "Philadelphia", + "name2": "Monmouth" + }, + { + "x1": 1017.8464, + "y1": 1017.1381, + "x2": 1033.8495, + "y2": 1070.3896, + "name": null, + "name1": "New Brunswick", + "name2": "Monmouth" + }, + { + "x1": 924.64522, + "y1": 988.37335, + "x2": 959.76472, + "y2": 981.72244, + "name": null, + "name1": "Philadelphia", + "name2": "New Brunswick" + }, + { + "x1": 930.01556, + "y1": 893.89157, + "x2": 978.18087, + "y2": 943.47351, + "name": null, + "name1": "Morristown", + "name2": "New Brunswick" + }, + { + "x1": 1023.785, + "y1": 945.87857, + "x2": 1045.4705999999999, + "y2": 913.01603, + "name": null, + "name1": "New Brunswick", + "name2": "New York" + }, + { + "x1": 1104.2595, + "y1": 880.08247, + "x2": 1148.1712, + "y2": 892.11777, + "name": null, + "name1": "New York", + "name2": "Long Island" + }, + { + "x1": 757.18708, + "y1": 913.72435, + "x2": 865.1994500000001, + "y2": 873.7064499999999, + "name": null, + "name1": "Reading", + "name2": "Morristown" + }, + { + "x1": 875.47542, + "y1": 846.43457, + "x2": 765.68563, + "y2": 792.2419600000001, + "name": null, + "name1": "Morristown", + "name2": "Wyoming Valley" + }, + { + "x1": 738.76731, + "y1": 823.40775, + "x2": 738.06503, + "y2": 897.7891099999999, + "name": null, + "name1": "Wyoming Valley", + "name2": "Reading" + }, + { + "x1": 631.8156, + "y1": 917.26592, + "x2": 685.64742, + "y2": 927.89062, + "name": null, + "name1": "Harrisburg", + "name2": "Reading" + }, + { + "x1": 472.16664, + "y1": 954.99649, + "x2": 513.40231, + "y2": 600.77474, + "name": null, + "name1": "Pittsburgh", + "name2": "Fort Niagara" + }, + { + "x1": 539.27641, + "y1": 576.31715, + "x2": 669.27295, + "y2": 609.48294, + "name": null, + "name1": "Fort Niagara", + "name2": "Genesee" + }, + { + "x1": 706.18851, + "y1": 658.02319, + "x2": 715.39658, + "y2": 749.39563, + "name": null, + "name1": "Genesee", + "name2": "Wyoming Valley" + }, + { + "x1": 132.80092, + "y1": 694.85067, + "x2": 461.55159, + "y2": 564.35025, + "name": null, + "name1": "Fort Detroit", + "name2": "Fort Niagara" + }, + { + "x1": 539.23485, + "y1": 548.69292, + "x2": 789.97769, + "y2": 496.6111, + "name": null, + "name1": "Fort Niagara", + "name2": "Oswego" + }, + { + "x1": 836.16704, + "y1": 488.73628, + "x2": 878.6646400000001, + "y2": 509.9893, + "name": null, + "name1": "Oswego", + "name2": "Fort Stanwix" + }, + { + "x1": 878.3123, + "y1": 537.08199, + "x2": 746.55996, + "y2": 607.91092, + "name": null, + "name1": "Fort Stanwix", + "name2": "Genesee" + }, + { + "x1": 957, + "y1": 560, + "x2": 991.63883, + "y2": 624.02415, + "name": null, + "name1": "Fort Stanwix", + "name2": "Albany" + }, + { + "x1": 1010.4299, + "y1": 624.89909, + "x2": 1022.0962999999999, + "y2": 570.90064, + "name": null, + "name1": "Albany", + "name2": "Saratoga" + }, + { + "x1": 1004.3885, + "y1": 700.522, + "x2": 999.43028, + "y2": 744.4374300000001, + "name": null, + "name1": "Albany", + "name2": "Westchester" + }, + { + "x1": 932.1405, + "y1": 839.35144, + "x2": 964.0133999999999, + "y2": 809.60347, + "name": null, + "name1": "Morristown", + "name2": "Westchester" + }, + { + "x1": 1023.5129, + "y1": 809.60227, + "x2": 1036.2685999999999, + "y2": 834.04814, + "name": null, + "name1": "Westchester", + "name2": "New York" + }, + { + "x1": 939.93195, + "y1": 871.22554, + "x2": 1024.9296, + "y2": 874.0588, + "name": null, + "name1": "Morristown", + "name2": "New York" + }, + { + "x1": 1086.554, + "y1": 834.75403, + "x2": 1104.9714000000001, + "y2": 805.7095599999999, + "name": null, + "name1": "New York", + "name2": "New Haven" + }, + { + "x1": 1031.3044, + "y1": 778.43648, + "x2": 1090.0944, + "y2": 774.89491, + "name": null, + "name1": "Westchester", + "name2": "New Haven" + }, + { + "x1": 1044.054, + "y1": 660.14813, + "x2": 1096.4692, + "y2": 660.85645, + "name": null, + "name1": "Albany", + "name2": "Springfield" + }, + { + "x1": 1175, + "y1": 699, + "x2": 1206.8827, + "y2": 748.06237, + "name": null, + "name1": "Springfield", + "name2": "Hartford" + }, + { + "x1": 1167.1178, + "y1": 782.06644, + "x2": 1185.6344, + "y2": 783.0435299999999, + "name": null, + "name1": "Hartford", + "name2": "Hartford" + }, + { + "x1": 1260.0896, + "y1": 777.72817, + "x2": 1292.672, + "y2": 777.01985, + "name": null, + "name1": "Hartford", + "name2": "Newport" + }, + { + "x1": 1302.6717, + "y1": 733.18767, + "x2": 1279.8807000000002, + "y2": 698.3136900000001, + "name": null, + "name1": "Newport", + "name2": "Lexington Concord" + }, + { + "x1": 1331.171, + "y1": 733.77117, + "x2": 1351.0039000000002, + "y2": 667.52295, + "name": null, + "name1": "Newport", + "name2": "Boston" + }, + { + "x1": 1296.7971, + "y1": 651.52331, + "x2": 1328.6711, + "y2": 640.35704, + "name": null, + "name1": "Lexington Concord", + "name2": "Boston" + }, + { + "x1": 1175.6754, + "y1": 664.60645, + "x2": 1217.9658000000002, + "y2": 664.8146399999999, + "name": null, + "name1": "Springfield", + "name2": "Lexington Concord" + }, + { + "x1": 1134.7598, + "y1": 561.23425, + "x2": 1131.1766, + "y2": 622.77415, + "name": null, + "name1": "Brattleboro", + "name2": "Springfield" + }, + { + "x1": 1394.6692, + "y1": 667.23127, + "x2": 1414.9186, + "y2": 707.23019, + "name": null, + "name1": "Boston", + "name2": "Barnstable" + }, + { + "x1": 1364.92, + "y1": 593.56667, + "x2": 1367.9199, + "y2": 485.77797000000004, + "name": null, + "name1": "Boston", + "name2": "Falmouth" + }, + { + "x1": 1338.8788, + "y1": 466.6535, + "x2": 1293.3386, + "y2": 488.06953, + "name": null, + "name1": "Falmouth", + "name2": "Concord" + }, + { + "x1": 1216.5493, + "y1": 512.27725, + "x2": 1181.9667, + "y2": 520.52701, + "name": null, + "name1": "Concord", + "name2": "Brattleboro" + }, + { + "x1": 1102.9274, + "y1": 532.27665, + "x2": 1072.3866, + "y2": 535.48497, + "name": null, + "name1": "Brattleboro", + "name2": "Saratoga" + }, + { + "x1": 1034.1376, + "y1": 433.48783, + "x2": 1035.5543, + "y2": 493.69446999999997, + "name": null, + "name1": "Ticonderoga", + "name2": "Saratoga" + }, + { + "x1": 1073.4544, + "y1": 390.62944, + "x2": 1126.5731, + "y2": 383.19275, + "name": null, + "name1": "Ticonderoga", + "name2": "Norwich" + }, + { + "x1": 1162.009, + "y1": 418.15493, + "x2": 1151.5095000000001, + "y2": 483.69482999999997, + "name": null, + "name1": "Norwich", + "name2": "Brattleboro" + }, + { + "x1": 1203.25, + "y1": 416.5, + "x2": 1232.1321, + "y2": 462.69531, + "name": null, + "name1": "Norwich", + "name2": "Concord" + }, + { + "x1": 1038.3875, + "y1": 237.28501, + "x2": 1037.6792, + "y2": 350.61516, + "name": null, + "name1": "Montreal", + "name2": "Ticonderoga" + }, + { + "x1": 1006.805, + "y1": 219.53548, + "x2": 828.01842, + "y2": 424.98807, + "name": null, + "name1": "Montreal", + "name2": "Oswego" + }, + { + "x1": 1073.095, + "y1": 174.99511, + "x2": 1130, + "y2": 122.25, + "name": null, + "name1": "Montreal", + "name2": "Quebec" + }, + { + "x1": 652.36158, + "y1": 1060.8876, + "x2": 670.77809, + "y2": 1096.1774, + "name": null, + "name1": "York", + "name2": "Frederick Town" + } + ], + "wilderness": [ + { + "x1": 90.648464, + "y1": 748.27474, + "x2": 167.28328, + "y2": 986.1757699999999, + "name": null, + "name1": "Fort Detroit", + "name2": "Bassett Town" + }, + { + "x1": 196.33447, + "y1": 1058.8038, + "x2": 170.79522, + "y2": 1212.0665999999999, + "name": null, + "name1": "Bassett Town", + "name2": "Point Pleasant" + }, + { + "x1": 1196, + "y1": 127, + "x2": 1346.2935, + "y2": 418.71331, + "name": "Quebec-Falmouth", + "name1": "Quebec", + "name2": "Falmouth" + } + ], + "sea": [ + { + "x1": 1040, + "y1": 195, + "x2": 1316.5, + "y2": 101.5, + "name": null, + "name1": "Montreal", + "name2": "Sea1" + }, + { + "x1": 1162.5, + "y1": 93.5, + "x2": 1316.5, + "y2": 101.5, + "name": null, + "name1": "Quebec", + "name2": "Sea1" + }, + { + "x1": 1374.0618, + "y1": 446.47501, + "x2": 1509.5, + "y2": 550, + "name": null, + "name1": "Falmouth", + "name2": "Sea2" + }, + { + "x1": 1367.5, + "y1": 629, + "x2": 1509.5, + "y2": 550, + "name": null, + "name1": "Boston", + "name2": "Sea2" + }, + { + "x1": 1434.3301, + "y1": 741.04565, + "x2": 1509.5, + "y2": 550, + "name": null, + "name1": "Barnstable", + "name2": "Sea2" + }, + { + "x1": 1330, + "y1": 772.5, + "x2": 1509.5, + "y2": 550, + "name": null, + "name1": "Newport", + "name2": "Sea2" + }, + { + "x1": 1186.3301, + "y1": 904.04565, + "x2": 1253.5, + "y2": 1016.5, + "name": null, + "name1": "Long Island", + "name2": "Sea3" + }, + { + "x1": 1128.0801, + "y1": 772.04565, + "x2": 1253.5, + "y2": 1016.5, + "name": null, + "name1": "New Haven", + "name2": "Sea3" + }, + { + "x1": 1064.5, + "y1": 874.5, + "x2": 1253.5, + "y2": 1016.5, + "name": null, + "name1": "New York", + "name2": "Sea3" + }, + { + "x1": 926, + "y1": 1116.5, + "x2": 1097.5, + "y2": 1210.5, + "name": null, + "name1": "Wilmington DE", + "name2": "Sea4" + }, + { + "x1": 886, + "y1": 997.5, + "x2": 1097.5, + "y2": 1210.5, + "name": null, + "name1": "Philadelphia", + "name2": "Sea4" + }, + { + "x1": 810.5, + "y1": 1142.5, + "x2": 986.5, + "y2": 1430.5, + "name": null, + "name1": "Baltimore", + "name2": "Sea5" + }, + { + "x1": 693.5, + "y1": 1254, + "x2": 986.5, + "y2": 1430.5, + "name": null, + "name1": "Alexandria", + "name2": "Sea5" + }, + { + "x1": 806.62952, + "y1": 1352.5383, + "x2": 986.5, + "y2": 1430.5, + "name": null, + "name1": "Yorktown", + "name2": "Sea5" + }, + { + "x1": 822, + "y1": 1487.5, + "x2": 986.5, + "y2": 1430.5, + "name": null, + "name1": "Norfolk", + "name2": "Sea5" + }, + { + "x1": 803.8092, + "y1": 1706.0598, + "x2": 677.5, + "y2": 2063.5, + "name": null, + "name1": "New Bern", + "name2": "Sea6" + }, + { + "x1": 715.5592, + "y1": 1852.3098, + "x2": 677.5, + "y2": 2063.5, + "name": null, + "name1": "Wilmington", + "name2": "Sea6" + }, + { + "x1": 484, + "y1": 2086, + "x2": 677.5, + "y2": 2063.5, + "name": null, + "name1": "Charleston", + "name2": "Sea6" + }, + { + "x1": 267, + "y1": 2307, + "x2": 399.5, + "y2": 2393.5, + "name": null, + "name1": "Savannah", + "name2": "Sea7" + }, + { + "x1": 220.5, + "y1": 2429.5, + "x2": 399.5, + "y2": 2393.5, + "name": null, + "name1": "St. Mary's", + "name2": "Sea7" + } + ], + "colony": [ + { + "x1": 1250.8815, + "y1": 498.65267, + "x2": 1503.0411, + "y2": 1042.6374, + "name": null, + "name1": "Concord", + "name2": "NH" + }, + { + "x1": 1136.1348, + "y1": 519.90207, + "x2": 1503.0411, + "y2": 1042.6374, + "name": null, + "name1": "Brattleboro", + "name2": "NH" + }, + { + "x1": 1172.9671, + "y1": 382.48926, + "x2": 1503.0411000000001, + "y2": 1042.6374, + "name": null, + "name1": "Norwich", + "name2": "NH" + }, + { + "x1": 1537.0402, + "y1": 1120.5519, + "x2": 1138.968, + "y2": 660.14813, + "name": null, + "name1": "MA", + "name2": "Springfield" + }, + { + "x1": 1255.1314, + "y1": 671.48115, + "x2": 1537.0402, + "y2": 1120.5519, + "name": null, + "name1": "Lexington Concord", + "name2": "MA" + }, + { + "x1": 1437.8763, + "y1": 745.14575, + "x2": 1537.0402, + "y2": 1120.5519, + "name": null, + "name1": "Barnstable", + "name2": "MA" + }, + { + "x1": 1365.6283, + "y1": 633.23222, + "x2": 1537.0402000000001, + "y2": 1120.5519, + "name": null, + "name1": "Boston", + "name2": "MA" + }, + { + "x1": 1365.6283, + "y1": 437.73771, + "x2": 1537.0402000000001, + "y2": 1120.5519, + "name": null, + "name1": "Falmouth", + "name2": "MA" + }, + { + "x1": 1333.0459, + "y1": 770.64503, + "x2": 1576.7057, + "y2": 1192.7998, + "name": null, + "name1": "Newport", + "name2": "RI" + }, + { + "x1": 1212.6326, + "y1": 780.56142, + "x2": 1503.0411, + "y2": 1197.0497, + "name": null, + "name1": "Hartford", + "name2": "CT" + }, + { + "x1": 1126.2184, + "y1": 772.06166, + "x2": 1503.0411, + "y2": 1197.0497, + "name": null, + "name1": "New Haven", + "name2": "CT" + }, + { + "x1": 1408.5453, + "y1": 1122.5186, + "x2": 1058.6385, + "y2": 878.85875, + "name": null, + "name1": "NY", + "name2": "New York" + }, + { + "x1": 1416.6269, + "y1": 1116.302, + "x2": 987.38895, + "y2": 781.97805, + "name": null, + "name1": "NY", + "name2": "Westchester" + }, + { + "x1": 1002.9718, + "y1": 665.81464, + "x2": 1416.6269, + "y2": 1116.302, + "name": null, + "name1": "Albany", + "name2": "NY" + }, + { + "x1": 1031.3044, + "y1": 535.48497, + "x2": 1416.6269, + "y2": 1116.302, + "name": null, + "name1": "Saratoga", + "name2": "NY" + }, + { + "x1": 1034.1376, + "y1": 390.98902, + "x2": 1416.6269, + "y2": 1116.302, + "name": null, + "name1": "Ticonderoga", + "name2": "NY" + }, + { + "x1": 913.72435, + "y1": 511.40231, + "x2": 1416.6269, + "y2": 1116.302, + "name": null, + "name1": "Fort Stanwix", + "name2": "NY" + }, + { + "x1": 807.47733, + "y1": 461.82037, + "x2": 1416.6269, + "y2": 1116.302, + "name": null, + "name1": "Oswego", + "name2": "NY" + }, + { + "x1": 708.31345, + "y1": 623.31583, + "x2": 1416.6269, + "y2": 1116.302, + "name": null, + "name1": "Genesee", + "name2": "NY" + }, + { + "x1": 488.73628, + "y1": 558.151, + "x2": 1416.6269, + "y2": 1116.302, + "name": null, + "name1": "Fort Niagara", + "name2": "NY" + }, + { + "x1": 83.580987, + "y1": 716.81321, + "x2": 1418.0435, + "y2": 1001.5552, + "name": null, + "name1": "Fort Detroit", + "name2": "Canada" + }, + { + "x1": 1415.2103, + "y1": 1226.7989, + "x2": 903.80796, + "y2": 859.89253, + "name": null, + "name1": "NJ", + "name2": "Morristown" + }, + { + "x1": 993.05545, + "y1": 970.38942, + "x2": 1415.2103, + "y2": 1226.7989, + "name": null, + "name1": "New Brunswick", + "name2": "NJ" + }, + { + "x1": 1045.4706, + "y1": 1114.8854, + "x2": 1415.2103000000002, + "y2": 1226.7988999999998, + "name": null, + "name1": "Monmouth", + "name2": "NJ" + }, + { + "x1": 1345.7956, + "y1": 1233.882, + "x2": 716.81321, + "y2": 934.97375, + "name": null, + "name1": "PA", + "name2": "Reading" + }, + { + "x1": 1345.7956, + "y1": 1233.882, + "x2": 628.98234, + "y2": 1031.3044, + "name": null, + "name1": "PA", + "name2": "York" + }, + { + "x1": 594.9833, + "y1": 912.30772, + "x2": 1345.7956, + "y2": 1233.882, + "name": null, + "name1": "Harrisburg", + "name2": "PA" + }, + { + "x1": 722.47972, + "y1": 784.8113, + "x2": 1345.7956, + "y2": 1233.882, + "name": null, + "name1": "Wyoming Valley", + "name2": "PA" + }, + { + "x1": 470.32013, + "y1": 995.88871, + "x2": 1345.7956, + "y2": 1233.882, + "name": null, + "name1": "Pittsburgh", + "name2": "PA" + }, + { + "x1": 179.91162, + "y1": 1025.6379, + "x2": 1345.7956, + "y2": 1233.882, + "name": null, + "name1": "Bassett Town", + "name2": "PA" + }, + { + "x1": 925.57679, + "y1": 1115.901, + "x2": 1425.4283, + "y2": 1302.2184, + "name": null, + "name1": "Wilmington DE", + "name2": "DE" + }, + { + "x1": 1333.2713, + "y1": 1315.2406, + "x2": 804.37031, + "y2": 1143.9488, + "name": null, + "name1": "MD", + "name2": "Baltimore" + }, + { + "x1": 672.14505, + "y1": 1127.9215, + "x2": 1333.2712999999999, + "y2": 1315.2405999999999, + "name": null, + "name1": "Frederick Town", + "name2": "MD" + }, + { + "x1": 478.8157, + "y1": 1132.93, + "x2": 1333.2712999999999, + "y2": 1315.2406, + "name": null, + "name1": "Fort Cumberland", + "name2": "MD" + }, + { + "x1": 805.37201, + "y1": 1355.3089, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Yorktown", + "name2": "VA" + }, + { + "x1": 820.39761, + "y1": 1484.529, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Norfolk", + "name2": "VA" + }, + { + "x1": 672.14505, + "y1": 1480.5222, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Petersburg", + "name2": "VA" + }, + { + "x1": 643.09556, + "y1": 1356.3106, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Richmond", + "name2": "VA" + }, + { + "x1": 695.1843, + "y1": 1262.1502, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Alexandria", + "name2": "VA" + }, + { + "x1": 479.81741, + "y1": 1295.2065, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Charlottesville", + "name2": "VA" + }, + { + "x1": 527.89932, + "y1": 1483.5273, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Lynch's Ferry", + "name2": "VA" + }, + { + "x1": 388.66212, + "y1": 1440.4539, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Fincastle", + "name2": "VA" + }, + { + "x1": 248.42321, + "y1": 1450.471, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Fort Chiswell", + "name2": "VA" + }, + { + "x1": 166.28328, + "y1": 1249.128, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Point Pleasant", + "name2": "VA" + }, + { + "x1": 97.165529, + "y1": 1485.5307, + "x2": 1279.1792, + "y2": 1372.3379, + "name": null, + "name1": "Abingdon", + "name2": "VA" + }, + { + "x1": 162.27645, + "y1": 1606.7372, + "x2": 1301.2167, + "y2": 1441.4556, + "name": null, + "name1": "Gilbert Town", + "name2": "NC" + }, + { + "x1": 335.57167, + "y1": 1597.7218, + "x2": 1301.2167, + "y2": 1441.4556, + "name": null, + "name1": "Salem", + "name2": "NC" + }, + { + "x1": 244.41638, + "y1": 1691.8823, + "x2": 1301.2167, + "y2": 1441.4556, + "name": null, + "name1": "Charlotte", + "name2": "NC" + }, + { + "x1": 517.88225, + "y1": 1624.7679, + "x2": 1301.2167, + "y2": 1441.4556, + "name": null, + "name1": "Hillsboro", + "name2": "NC" + }, + { + "x1": 646.10068, + "y1": 1711.9164, + "x2": 1301.2167, + "y2": 1441.4556, + "name": null, + "name1": "Wake (Raleigh)", + "name2": "NC" + }, + { + "x1": 802.36689, + "y1": 1711.9164, + "x2": 1301.2167, + "y2": 1441.4556, + "name": null, + "name1": "New Bern", + "name2": "NC" + }, + { + "x1": 713.21502, + "y1": 1850.1519, + "x2": 1301.2167, + "y2": 1441.4556, + "name": null, + "name1": "Wilmington", + "name2": "NC" + }, + { + "x1": 1258.1433, + "y1": 1528.6041, + "x2": 615.04778, + "y2": 1957.3345, + "name": null, + "name1": "SC", + "name2": "Georgetown" + }, + { + "x1": 482.82253, + "y1": 2090.5614, + "x2": 1258.1433, + "y2": 1528.6041, + "name": null, + "name1": "Charleston", + "name2": "SC" + }, + { + "x1": 442.75427, + "y1": 1942.3089, + "x2": 1258.1433, + "y2": 1528.6041, + "name": null, + "name1": "Eutaw Springs", + "name2": "SC" + }, + { + "x1": 534.91126, + "y1": 1833.1229, + "x2": 1258.1433, + "y2": 1528.6041, + "name": null, + "name1": "Cheraw", + "name2": "SC" + }, + { + "x1": 379.64676, + "y1": 1809.0819, + "x2": 1258.1433, + "y2": 1528.6041, + "name": null, + "name1": "Camden", + "name2": "SC" + }, + { + "x1": 257.43857, + "y1": 1972.3601, + "x2": 1258.1433, + "y2": 1528.6041, + "name": null, + "name1": "Ninety Six", + "name2": "SC" + }, + { + "x1": 114.19454, + "y1": 1813.0887, + "x2": 1258.1433, + "y2": 1528.6041, + "name": null, + "name1": "Fort Prince George", + "name2": "SC" + }, + { + "x1": 173.29522, + "y1": 2094.5683, + "x2": 1208.058, + "y2": 1596.7201, + "name": null, + "name1": "Augusta", + "name2": "GA" + }, + { + "x1": 266.45392, + "y1": 2315.9454, + "x2": 1208.058, + "y2": 1596.7201, + "name": null, + "name1": "Savannah", + "name2": "GA" + }, + { + "x1": 220.37543, + "y1": 2437.1519, + "x2": 1208.058, + "y2": 1596.7201, + "name": null, + "name1": "St. Mary's", + "name2": "GA" + }, + { + "x1": 1408.5453, + "y1": 1122.5186, + "x2": 1186.3301, + "y2": 904.04565, + "name": null, + "name1": "NY", + "name2": "Long Island" + }, + { + "x1": 886, + "y1": 997.5, + "x2": 1345.7956, + "y2": 1233.882, + "name": null, + "name1": "Philadelphia", + "name2": "PA" + }, + { + "x1": 1162.5, + "y1": 93.5, + "x2": 1418.0435, + "y2": 1001.5552, + "name": null, + "name1": "Quebec", + "name2": "Canada" + }, + { + "x1": 1040, + "y1": 195, + "x2": 1418.0435, + "y2": 1001.5552, + "name": null, + "name1": "Montreal", + "name2": "Canada" + } + ] + }, + "labels": [ + { + "x": 1527.1014, + "y": 2281.8875, + "name": "9" + }, + { + "x": 1468.5017, + "y": 2409.605, + "name": "1783" + }, + { + "x": 1399.8848, + "y": 2412.6101, + "name": "1782" + }, + { + "x": 1329.2645, + "y": 2408.6033, + "name": "1781" + }, + { + "x": 1265.1553, + "y": 2410.1057, + "name": "1780" + }, + { + "x": 1201.0461, + "y": 2410.1057, + "name": "1779" + }, + { + "x": 1138.4395, + "y": 2409.605, + "name": "1778" + }, + { + "x": 1062.8105, + "y": 2408.1025, + "name": "1777" + }, + { + "x": 998.2005, + "y": 2408.6033, + "name": "1776" + }, + { + "x": 928.08105, + "y": 2411.1077, + "name": "1775" + }, + { + "x": 1476.0145, + "y": 2284.8926, + "name": "8" + }, + { + "x": 1416.9138, + "y": 2286.896, + "name": "7" + }, + { + "x": 1342.2867, + "y": 2285.3933, + "name": "6" + }, + { + "x": 1264.6544, + "y": 2282.3882, + "name": "5" + }, + { + "x": 1213.5674, + "y": 2279.884, + "name": "4" + }, + { + "x": 1144.4497, + "y": 2280.3848, + "name": "3" + }, + { + "x": 1082.3439, + "y": 2282.3882, + "name": "2" + }, + { + "x": 1004.2108, + "y": 2285.8943, + "name": "1" + }, + { + "x": 932.08789, + "y": 2283.8909, + "name": "0" + }, + { + "x": 166.09312, + "y": 365.55655, + "name": "French Reinforcements" + }, + { + "x": 395.46118, + "y": 363.54544, + "name": "American Leader Reinforcements" + }, + { + "x": 1308.392, + "y": 100.05029, + "name": "Sea1" + }, + { + "x": 1503.4926, + "y": 111.18933, + "name": "Captured Generals" + }, + { + "x": 1155, + "y": 95, + "name": "Quebec" + }, + { + "x": 1030, + "y": 200, + "name": "Montreal" + }, + { + "x": 1170, + "y": 380, + "name": "Norwich" + }, + { + "x": 1030, + "y": 400, + "name": "Ticonderoga" + }, + { + "x": 815, + "y": 470, + "name": "Oswego" + }, + { + "x": 910, + "y": 515, + "name": "Fort Stanwix" + }, + { + "x": 1025, + "y": 540, + "name": "Saratoga" + }, + { + "x": 1145, + "y": 535, + "name": "Brattleboro" + }, + { + "x": 1240, + "y": 500, + "name": "Concord" + }, + { + "x": 1363.8234, + "y": 463.28925, + "name": "Falmouth" + }, + { + "x": 1363.3225, + "y": 632.07678, + "name": "Boston" + }, + { + "x": 1428.9343, + "y": 738.75854, + "name": "Barnstable" + }, + { + "x": 1330, + "y": 772.5, + "name": "Newport" + }, + { + "x": 1254.1365, + "y": 673.64758, + "name": "Lexington Concord" + }, + { + "x": 1215.0699, + "y": 800.36346, + "name": "Hartford" + }, + { + "x": 1110.3916, + "y": 778.82678, + "name": "New Haven" + }, + { + "x": 1121.9113, + "y": 655.61688, + "name": "Springfield" + }, + { + "x": 997.19879, + "y": 667.63739, + "name": "Albany" + }, + { + "x": 989.18512, + "y": 794.85413, + "name": "Westchester" + }, + { + "x": 1057.802, + "y": 875.99231, + "name": "New York" + }, + { + "x": 1188.0239, + "y": 912.55463, + "name": "Long Island" + }, + { + "x": 1001.3301, + "y": 977.54565, + "name": "New Brunswick" + }, + { + "x": 1033.7611, + "y": 1103.8805, + "name": "Monmouth" + }, + { + "x": 893.0213, + "y": 871.98547, + "name": "Morristown" + }, + { + "x": 715.7193, + "y": 630.07336, + "name": "Genesee" + }, + { + "x": 501.56177, + "y": 561.97504, + "name": "Fort Niagara" + }, + { + "x": 83.14164, + "y": 710.2099, + "name": "Fort Detroit" + }, + { + "x": 181.30887, + "y": 1032.7594, + "name": "Bassett Town" + }, + { + "x": 471.30289, + "y": 1001.2056, + "name": "Pittsburgh" + }, + { + "x": 598.0188, + "y": 923.07251, + "name": "Harrisburg" + }, + { + "x": 723.75372, + "y": 936.08838, + "name": "Reading" + }, + { + "x": 624.56396, + "y": 1030.756, + "name": "York" + }, + { + "x": 718.72443, + "y": 786.84045, + "name": "Wyoming Valley" + }, + { + "x": 872.98718, + "y": 1009.2193, + "name": "Philadelphia" + }, + { + "x": 909.04865, + "y": 1123.4138, + "name": "Wilmington DE" + }, + { + "x": 800.36346, + "y": 1153.465, + "name": "Baltimore" + }, + { + "x": 661.62714, + "y": 1143.448, + "name": "Frederick Town" + }, + { + "x": 682.16211, + "y": 1260.1469, + "name": "Alexandria" + }, + { + "x": 467.79694, + "y": 1297.21, + "name": "Charlottesville" + }, + { + "x": 636.08362, + "y": 1367.8302, + "name": "Richmond" + }, + { + "x": 808.37714, + "y": 1357.3123, + "name": "Yorktown" + }, + { + "x": 819.89679, + "y": 1495.5479, + "name": "Norfolk" + }, + { + "x": 660.62543, + "y": 1496.5496, + "name": "Petersburg" + }, + { + "x": 512.87372, + "y": 1485.5306, + "name": "Lynch's Ferry" + }, + { + "x": 386.15784, + "y": 1446.4642, + "name": "Fincastle" + }, + { + "x": 237.90529, + "y": 1447.9667, + "name": "Fort Chiswell" + }, + { + "x": 160.7739, + "y": 1260.1469, + "name": "Point Pleasant" + }, + { + "x": 91.656143, + "y": 1496.5496, + "name": "Abingdon" + }, + { + "x": 162.8092, + "y": 1603.8098, + "name": "Gilbert Town" + }, + { + "x": 341.58191, + "y": 1604.7338, + "name": "Salem" + }, + { + "x": 514.87714, + "y": 1625.2688, + "name": "Hillsboro" + }, + { + "x": 646.3092, + "y": 1707.0598, + "name": "Wake (Raleigh)" + }, + { + "x": 815.88995, + "y": 1717.4258, + "name": "New Bern" + }, + { + "x": 713.71588, + "y": 1857.1638, + "name": "Wilmington" + }, + { + "x": 605.0307, + "y": 1954.3293, + "name": "Georgetown" + }, + { + "x": 523.89252, + "y": 1833.6237, + "name": "Cheraw" + }, + { + "x": 368.62799, + "y": 1816.5947, + "name": "Camden" + }, + { + "x": 249.42491, + "y": 1701.8993, + "name": "Charlotte" + }, + { + "x": 110.68857, + "y": 1818.5981, + "name": "Fort Prince George" + }, + { + "x": 252.93089, + "y": 1970.8575, + "name": "Ninety Six" + }, + { + "x": 434.7406, + "y": 1951.3242, + "name": "Eutaw Springs" + }, + { + "x": 478.8157, + "y": 2099.0759, + "name": "Charleston" + }, + { + "x": 264.4505, + "y": 2315.4446, + "name": "Savannah" + }, + { + "x": 179.8063, + "y": 2104.0845, + "name": "Augusta" + }, + { + "x": 215.3669, + "y": 2438.6543, + "name": "St. Mary's" + }, + { + "x": 1008.5179, + "y": 2050.7935, + "name": "BRC" + }, + { + "x": 1231.9423, + "y": 2050.9939, + "name": "ARC1" + }, + { + "x": 1466.2352, + "y": 2057.8745, + "name": "ARC2" + }, + { + "x": 1465, + "y": 1776, + "name": "British Reinforcement Box" + }, + { + "x": 1167.4889, + "y": 1789.0479, + "name": "War Ends Card" + }, + { + "x": 1197.0392, + "y": 1587.2039, + "name": "GA" + }, + { + "x": 1252.1331, + "y": 1520.0896, + "name": "SC" + }, + { + "x": 1300, + "y": 1443.5, + "name": "NC" + }, + { + "x": 1290.6989, + "y": 1383.3567, + "name": "VA" + }, + { + "x": 1339, + "y": 1302.5, + "name": "MD" + }, + { + "x": 1407.3976, + "y": 1313.7382, + "name": "DE" + }, + { + "x": 1414.9104, + "y": 1234.1024, + "name": "NJ" + }, + { + "x": 1339, + "y": 1230, + "name": "PA" + }, + { + "x": 1504.0623, + "y": 1195.0358, + "name": "CT" + }, + { + "x": 1577.6877, + "y": 1194.535, + "name": "RI" + }, + { + "x": 1534.1135, + "y": 1122.913, + "name": "MA" + }, + { + "x": 1406.8969, + "y": 1122.4121, + "name": "NY" + }, + { + "x": 1498, + "y": 1043, + "name": "NH" + }, + { + "x": 1416, + "y": 1003.5, + "name": "Canada" + }, + { + "x": 1509.5, + "y": 550, + "name": "Sea2" + }, + { + "x": 1253.5, + "y": 1016.5, + "name": "Sea3" + }, + { + "x": 1088.8549, + "y": 1218.0751, + "name": "Sea4" + }, + { + "x": 981.17151, + "y": 1441.9565, + "name": "Sea5" + }, + { + "x": 671.14337, + "y": 2067.0212, + "name": "Sea6" + }, + { + "x": 393.67065, + "y": 2394.5793, + "name": "Sea7" + }, + { + "x": 585, + "y": 385, + "name": "Continental Corgress Dispersed Box" + }, + { + "x": 479.92914, + "y": 1140.5603, + "name": "Fort Cumberland" + } + ] +}
\ No newline at end of file diff --git a/tools/layout.svg b/tools/layout.svg index 506d903..9805857 100644 --- a/tools/layout.svg +++ b/tools/layout.svg @@ -40,9 +40,9 @@ inkscape:window-height="480" id="namedview8" showgrid="false" - inkscape:zoom="0.5" - inkscape:cx="1894.5029" - inkscape:cy="1424.4221" + inkscape:zoom="0.71155804" + inkscape:cx="871.00863" + inkscape:cy="553.26207" inkscape:current-layer="svg6" inkscape:snap-center="false" inkscape:snap-others="true" @@ -55,31 +55,32 @@ <image sodipodi:absref="/home/tor/src/rally/public/washingtons-war/tools/mapmask.png" xlink:href="mapmask.png" - x="0" - y="0" - width="1650" - height="2550" - image-rendering="pixelated" - sodipodi:insensitive="true" + style="display:inline" id="image2" - style="display:inline" /> + sodipodi:insensitive="true" + image-rendering="pixelated" + height="2550" + width="1650" + y="0" + x="0" /> <image sodipodi:absref="/home/tor/src/rally/public/washingtons-war/tools/map75.png" xlink:href="map75.png" - x="0" - y="0" - width="1650" - height="2550" - image-rendering="pixelated" - opacity="0.535871" - id="image4" - style="display:inline" + sodipodi:insensitive="true" inkscape:label="texture" - sodipodi:insensitive="true" /> + style="display:inline" + id="image4" + opacity="0.535871" + image-rendering="pixelated" + height="2550" + width="1650" + y="0" + x="0" /> <g id="g2693" - inkscape:label="fortress" - style="display:none"> + inkscape:label="fortified_port" + style="display:none" + sodipodi:insensitive="true"> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" id="rect887" @@ -112,7 +113,8 @@ <g id="g2712" inkscape:label="winter_quarters" - style="display:none"> + style="display:none" + sodipodi:insensitive="true"> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" id="rect845" @@ -235,8 +237,9 @@ </g> <g id="g2687" - inkscape:label="space" - style="display:none"> + inkscape:label="regular" + style="display:none" + sodipodi:insensitive="true"> <circle style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" id="circle910" @@ -587,7 +590,8 @@ <g id="g2555" inkscape:label="sea" - style="display:inline"> + style="display:inline" + sodipodi:insensitive="true"> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" id="rect1000" @@ -641,7 +645,8 @@ <g id="g2546" inkscape:label="colony" - style="display:inline"> + style="display:inline" + sodipodi:insensitive="true"> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" id="rect1016" @@ -744,85 +749,83 @@ <g id="g2584" inkscape:label="box" - style="display:inline" - sodipodi:insensitive="true"> + style="display:inline"> + <rect + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:2.99946" + id="rect1092" + width="214" + height="206" + x="1358" + y="1673" + inkscape:label="British Reinforcement Box" /> + <rect + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3.00286" + id="rect1096" + width="214" + height="206" + x="294" + y="258" + inkscape:label="American Leader Reinforcements" /> + <rect + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3.08098" + id="rect1098" + width="214" + height="207" + x="63" + y="258" + inkscape:label="French Reinforcements" /> <rect style="opacity:1;fill:#000000;fill-opacity:0.46748;stroke:none;stroke-width:3" id="rect1606" width="88" height="104" x="542" - y="332" /> + y="332" + inkscape:label="Continental Congress Dispersed" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1044" - width="64" - height="71" - x="905" - y="2241" /> - <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1046" - width="64" - height="71" - x="971" - y="2241" /> - <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1048" - width="64" - height="71" - x="1037" - y="2241" /> - <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1050" - width="64" - height="71" - x="1103" - y="2241" /> - <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1052" - width="65" - height="71" - x="1169" - y="2241" /> - <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1054" - width="64" - height="71" - x="1236" - y="2241" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3.23342" + id="rect1094" + width="205" + height="136" + x="1400" + y="46" + inkscape:label="Captured Generals" /> + </g> + <g + id="g607" + inkscape:label="Card Boxes"> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1056" - width="64" - height="71" - x="1302" - y="2241" /> + id="rect1084" + width="199" + height="277" + x="904" + y="1915" /> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1058" - width="64" - height="71" - x="1368" - y="2241" /> + id="rect1086" + width="199" + height="277" + x="1137" + y="1915" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1060" - width="65" - height="71" - x="1434" - y="2241" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:2.97641" + id="rect1088" + width="199.00003" + height="277" + x="1370" + y="1915" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1062" - width="63" - height="71" - x="1501" - y="2241" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3.0365" + id="rect1090" + width="278" + height="198.99998" + x="1027" + y="1679" /> + </g> + <g + id="g580" + inkscape:label="Game Turn"> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" id="rect1066" @@ -886,62 +889,80 @@ height="72" x="1433" y="2368" /> + </g> + <g + id="g569" + inkscape:label="French Alliance Track"> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1084" - width="199" - height="277" - x="904" - y="1915" /> + id="rect1044" + width="64" + height="71" + x="905" + y="2241" /> <rect style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" - id="rect1086" - width="199" - height="277" - x="1137" - y="1915" /> + id="rect1046" + width="64" + height="71" + x="971" + y="2241" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:2.97641" - id="rect1088" - width="199.00003" - height="277" - x="1370" - y="1915" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" + id="rect1048" + width="64" + height="71" + x="1037" + y="2241" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3.0365" - id="rect1090" - width="278" - height="198.99998" - x="1027" - y="1679" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" + id="rect1050" + width="64" + height="71" + x="1103" + y="2241" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:2.99946" - id="rect1092" - width="214" - height="206" - x="1358" - y="1673" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" + id="rect1052" + width="65" + height="71" + x="1169" + y="2241" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3.23342" - id="rect1094" - width="205" - height="136" - x="1400" - y="46" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" + id="rect1054" + width="64" + height="71" + x="1236" + y="2241" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3.00286" - id="rect1096" - width="214" - height="206" - x="294" - y="258" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" + id="rect1056" + width="64" + height="71" + x="1302" + y="2241" /> <rect - style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3.08098" - id="rect1098" - width="214" - height="207" - x="63" - y="258" /> + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" + id="rect1058" + width="64" + height="71" + x="1368" + y="2241" /> + <rect + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" + id="rect1060" + width="65" + height="71" + x="1434" + y="2241" /> + <rect + style="opacity:0.99;fill:#ff0000;fill-opacity:0.46748;stroke:none;stroke-width:3" + id="rect1062" + width="63" + height="71" + x="1501" + y="2241" /> </g> <g id="g2117" @@ -1202,36 +1223,43 @@ sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 759.31202,949.84833 92.08074,31.1658" - id="path1918" /> + d="m 759.31202,949.84833 90.94423,35.42773" + id="path1918" + sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 905.22459,1026.3462 1.41662,2.8332 13.45796,48.8737" - id="path1920" /> + d="m 908.63011,1030.8842 11.46906,47.1689" + id="path1920" + sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 845.72626,1138.2597 46.74868,-9.9164" - id="path1922" /> + d="m 849.98819,1137.6914 36.80418,-8.4957" + id="path1922" + sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 913.01603,1019.9714 96.33067,71.5396" - id="path1924" /> + d="m 920.40337,1023.0968 83.54493,65.0047" + id="path1924" + sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 1017.8464,1017.1381 17.7079,58.0817" - id="path1926" /> + d="m 1017.8464,1017.1381 16.0031,53.2515" + id="path1926" + sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 920.09917,990.93051 39.66555,-9.20807" - id="path1928" /> + d="m 924.64522,988.37335 35.1195,-6.65091" + id="path1928" + sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 930.01556,893.89157 48.16531,49.58194" id="path1930" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="m 1027.7628,947.01508 17.7078,-33.99905" - id="path1932" /> + d="m 1023.785,945.87857 21.6856,-32.86254" + id="path1932" + sodipodi:nodetypes="cc" /> <path style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 1104.2595,880.08247 43.9117,12.0353" @@ -1451,7 +1479,8 @@ style="opacity:1;fill:none;fill-opacity:0.46748;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:5, 5;stroke-dashoffset:0;stroke-opacity:1" d="m 1196,127 c 74.0155,102.10344 61.3742,165.34334 150.2935,291.71331" id="path2123" - sodipodi:nodetypes="cc" /> + sodipodi:nodetypes="cc" + inkscape:label="Quebec-Falmouth" /> </g> <g id="g2298" @@ -1542,7 +1571,8 @@ <g id="g2530" inkscape:label="colony" - style="display:none"> + style="display:none" + sodipodi:insensitive="true"> <path style="opacity:1;fill:none;fill-opacity:1;stroke:#f50000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:3, 3;stroke-dashoffset:0;stroke-opacity:1" d="m 1250.8815,498.65267 252.1596,543.98473" @@ -1816,7 +1846,8 @@ <g id="g1810" inkscape:label="labels" - style="display:none"> + style="display:inline" + sodipodi:insensitive="true"> <text xml:space="preserve" style="font-size:14px;font-family:sans-serif;-inkscape-font-specification:sans-serif;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:12, 6, 3, 6;stroke-dashoffset:0;stroke-opacity:1" @@ -2406,7 +2437,7 @@ sodipodi:role="line" id="tspan1376" x="909.04865" - y="1123.4138">Wilmington</tspan></text> + y="1123.4138">Wilmington DE</tspan></text> <text xml:space="preserve" style="font-size:14px;font-family:sans-serif;-inkscape-font-specification:sans-serif;opacity:1;fill:#000000;fill-opacity:0.46748;stroke:none;stroke-width:3" diff --git a/tools/parse-layout-old.js b/tools/parse-layout-old.js new file mode 100644 index 0000000..b382849 --- /dev/null +++ b/tools/parse-layout-old.js @@ -0,0 +1,466 @@ +"use strict" + +/* COMMON PARSING */ + +const fs = require("fs") + +let points = {} +let circles = {} +let rects = {} +let edges = {} +let labels = [] + +let mode, name, x, y, w, h, cx, cy, rx, ry, x2, y2 + +function array_insert(array, index, item) { + for (let i = array.length; i > index; --i) + array[i] = array[i - 1] + array[index] = item +} + +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 add_point(x, y) { + if (!(name in points)) + points[name] = [] + points[name].push({x,y}) +} + +function add_circle(cx, cy, rx, ry) { + if (!(name in circles)) + circles[name] = [] + circles[name].push({x:cx-rx,y:cy-ry,w:rx*2,h:ry*2}) +} + +function add_rect(x, y, w, h) { + if (!(name in rects)) + rects[name] = [] + rects[name].push({x,y,w,h}) +} + +function add_edge(x1, y1, x2, y2) { + if (!(name in edges)) + edges[name] = [] + edges[name].push({x1,y1,x2,y2}) +} + +function flush() { + if (mode === 'path') { + add_edge(x, y, x2, y2) + } + if (mode === 'rect') { + add_rect(x, y, w, h) + add_point(x + w/2, y + h/2) + } + if (mode === 'circle') { + add_circle(cx, cy, rx, ry) + add_point(cx, cy) + } + x = y = x2 = y2 = w = h = cx = cy = rx = ry = 0 +} + +function parse_path_data(path) { + let cx = 0 + let cy = 0 + let abs = 0 + for (let i = 0; i < path.length;) { + switch (path[i]) { + case 'M': + x2 = x = cx = Number(path[i+1]) + y2 = y = cy = Number(path[i+2]) + i += 3 + abs = true + break + case 'm': + x2 = x = cx = cx + Number(path[i+1]) + y2 = y = cy = cy + Number(path[i+2]) + i += 3 + abs = false + break + case 'C': + x2 = cx = Number(path[i+5]) + y2 = cy = Number(path[i+6]) + i += 7 + abs = true + break + case 'L': + i += 1 + abs = true + break + case 'H': + x2 = cx = Number(path[i+1]) + i += 2 + abs = true + break + case 'V': + y2 = cy = Number(path[i+1]) + i += 2 + abs = true + break + case 'c': + x2 = cx = cx + Number(path[i+5]) + y2 = cy = cy + Number(path[i+6]) + i += 7 + abs = false + break + case 'l': + i += 1 + abs = false + break + case 'h': + x2 = cx = cx + Number(path[i+1]) + i += 2 + abs = false + break + case 'v': + y2 = cy = cy + Number(path[i+1]) + i += 2 + abs = false + break + default: + if (abs) { + x2 = cx = Number(path[i+0]) + y2 = cy = Number(path[i+1]) + } else { + x2 = cx = cx + Number(path[i+0]) + y2 = cy = cy + Number(path[i+1]) + } + i += 2 + break + } + } +} + +for (let line of fs.readFileSync("tools/layout.svg", "utf-8").split("\n")) { + line = line.trim() + if (line.startsWith("<rect")) { + flush() + mode = "rect" + x = y = w = h = 0 + } + else if (line.startsWith("<ellipse") || line.startsWith("<circle")) { + flush() + mode = "circle" + cx = cy = rx = ry = 0 + } + else if (line.startsWith("<path")) { + flush() + mode = "path" + } + else if (line.startsWith("<g")) { + flush() + mode = "g" + } + else if (line.startsWith('x="')) + x = (Number(line.split('"')[1])) + else if (line.startsWith('y="')) + y = (Number(line.split('"')[1])) + else if (line.startsWith('width="')) + w = (Number(line.split('"')[1])) + else if (line.startsWith('height="')) + h = (Number(line.split('"')[1])) + else if (line.startsWith('cx="')) + cx = (Number(line.split('"')[1])) + else if (line.startsWith('cy="')) + cy = (Number(line.split('"')[1])) + else if (line.startsWith('r="')) + rx = ry = (Number(line.split('"')[1])) + else if (line.startsWith('rx="')) + rx = (Number(line.split('"')[1])) + else if (line.startsWith('ry="')) + ry = (Number(line.split('"')[1])) + else if (line.startsWith('inkscape:label="') && mode === "g") + name = line.split('"')[1] + else if (line.startsWith('d="')) + parse_path_data(line.split('"')[1].split(/[ ,]/)) + if (line.includes("</tspan>")) { + let name = line.replace(/^[^>]*>/, "").replace(/<\/tspan.*/, "") + labels.push({x, y, name}) + } +} + +flush() + +function find_closest_label(x, y) { + let nd = Infinity, nn = null + + for (let n of labels) { + let d = Math.hypot(n.x - x, n.y - y) + if (d < nd) { + nd = d + nn = n + } + } + + if (!nn) { + console.log("NOT FOUND", x, y) + return null + } + + if (nd > 50) { + console.log("NO LABEL", x, y, nd) + return null + } + + return nn.name +} + +function find_closest_node(nodes, x, y) { + let nd = Infinity, nn = -1 + + for (let i = 0; i < nodes.length; ++i) { + let n = nodes[i] + let d = Math.hypot(n.x - x, n.y - y) + if (d < nd) { + nd = d + nn = i + } + } + + if (nd > 100) + return -1 + + return nn +} + +function label_boxes(top) { + for (let key in top) { + console.log("BOX", key) + for (let item of top[key]) + item.name = find_closest_label(item.x + item.w/2, item.y + item.h/2) + } +} + +function label_points(top) { + for (let key in top) { + console.log("POINT", key) + for (let item of top[key]) + item.name = find_closest_label(item.x, item.y) + } +} + +label_boxes(rects) +label_boxes(circles) +label_points(points) + +function connect_edge_2way(node_list, edge_name) { + console.log("EDGE", edge_name) + for (let e of edges[edge_name]) { + let a = find_closest_node(node_list, e.x1, e.y1) + let b = find_closest_node(node_list, e.x2, e.y2) + if (a < 0 || b < 0) + console.log("CANNOT FIND EDGE", e, node_list[a], node_list[b]) + if (!node_list[a][edge_name]) + node_list[a][edge_name] = [] + if (!node_list[b][edge_name]) + node_list[b][edge_name] = [] + set_add(node_list[a][edge_name], b) + set_add(node_list[b][edge_name], a) + } +} + +function connect_edge_1way(a_list, b_list, edge_name, prop_name) { + console.log("EDGE", edge_name) + for (let e of edges[edge_name]) { + let a1 = find_closest_node(a_list, e.x1, e.y1) + let a2 = find_closest_node(a_list, e.x2, e.y2) + let b1 = find_closest_node(b_list, e.x1, e.y1) + let b2 = find_closest_node(b_list, e.x2, e.y2) + let a = a1 >= 0 ? a1 : a2 + let b = b1 >= 0 ? b1 : b2 + if (a < 0 || b < 0) + console.log("CANNOT FIND EDGE", e, a_list[a], b_list[b]) + if (!a_list[a][prop_name]) + a_list[a][prop_name] = [] + set_add(a_list[a][prop_name], b) + } +} + +/* WASHINGTON'S WAR */ + +function sort_alpha(list) { + list.sort((a,b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0) +} + +sort_alpha(points.fortress) +sort_alpha(points.winter_quarters) +sort_alpha(points.space) +points.colony.reverse() + +const data = {} + +data.spaces = [ ...points.fortress, ...points.winter_quarters, ...points.space ] +data.colonies = [ ...points.colony ] +data.seas = [ ...points.sea ] + +connect_edge_2way(data.spaces, "path") +connect_edge_2way(data.spaces, "wilderness") +connect_edge_1way(data.colonies, data.spaces, "colony", "spaces") +connect_edge_1way(data.seas, data.spaces, "sea", "spaces") + +console.log(JSON.stringify(data,0,4)) + +/* +function find_closest_node(list, x, y) { + let nd = Infinity, nn = null + + for (let n of list) { + let d = Math.hypot(n.x - x, n.y - y) + if (d < nd) { + nd = d + nn = n + } + } + + if (!nn) { + console.log("NOT FOUND", x, y) + return [ null, 0 ] + } + + return [ nn, nd ] +} + +function find_enclosing_rect(list, x, y) { + for (let [x1, y1, x2, y2] of list) { + if (x >= x1 && x <= x2) + if (y >= y1 && y <= y2) + return true + } + return false +} + + +function make_spaces(points) { + let spaces = [] + for (let [x,y] of points) { + let [ name, dist ] = find_closest_node(labels, x, y) + if (dist > 50) console.log("DISTANCE TOO FAR", x, y, dist) + + spaces.push({ name: name.name, x, y }) + } + return spaces +} + +let data = {} + +data.spaces = make_spaces([ ...points.fortress, ...points.winter_quarters, ...points.space ]) + +console.log(data) + +function find_closest_point(x, y) { + let nd = Infinity, nn = -1 + + for (let i = 0; i < points.length; ++i) { + let n = points[i] + let d = Math.hypot(n.x - x, n.y - y) + if (d < nd) { + nd = d + nn = i + } + } + + return nn +} + +// FIND and label all points! +let all_labels = labels.slice() +let cities = [] +for (let key in points) { + for (let [x, y] of points[key]) { + let [ node, dist ] = find_closest_node(labels, x, y) + if (dist > 15) { + console.log("DISTANCE TOO FAR", key,x,y, "dist=" + dist, "name=" + node.name) + } + if (node) { + labels = labels.filter(x => x !== node) + let suit = "UNKNOWN" + + if (find_enclosing_rect(rects.$CLUBS, x, y)) + suit = CLUBS + else if (find_enclosing_rect(rects.$HEARTS, x, y)) + suit = HEARTS + else if (find_enclosing_rect(rects.$DIAMONDS, x, y)) + suit = DIAMONDS + else if (find_enclosing_rect(rects.$SPADES, x, y)) + suit = SPADES + else + console.log("NOT ASSIGNED SUIT", x, y) + + let country = "UNKNOWN" + if (find_enclosing_rect(rects.$Empire, x, y)) { + country = EMPIRE + } + else if (find_enclosing_rect(rects.$Austria, x, y)) { + country = AUSTRIA + } + else if (find_enclosing_rect(rects.$Hanover, x, y)) { + country = HANOVER + } + else if (find_enclosing_rect(rects.$Saxony, x, y)) { + country = SAXONY + } + else if (find_enclosing_rect(rects.$Sweden, x, y)) { + country = SWEDEN + } + else if (find_enclosing_rect(rects.$Poland, x, y)) { + country = POLAND + } + else if (find_enclosing_rect(rects.$Prussia, x, y)) { + country = PRUSSIA + } + + if (country === "UNKNOWN") + console.log("no country:", node) + + cities.push({ + name: node.name, + country, + suit, + type: key, + x: Math.round(x), + y: Math.round(y), + adjacent: [], + major_roads: [], + roads: [], + }) + } else { + let [ dupname, dupdist ] = find_closest_node(all_labels, x, y) + console.log("ALREADY USED", dupname, dupdist, x, y) + } + } +} + +for (let e of edges.major_road) { + let a = find_closest_point(e.x1, e.y1) + let b = find_closest_point(e.x2, e.y2) + set_add(cities[a].major_roads, b) + set_add(cities[b].major_roads, a) + set_add(cities[a].adjacent, b) + set_add(cities[b].adjacent, a) +} + +for (let e of edges.road) { + let a = find_closest_point(e.x1, e.y1) + let b = find_closest_point(e.x2, e.y2) + set_add(cities[a].roads, b) + set_add(cities[b].roads, a) + set_add(cities[a].adjacent, b) + set_add(cities[b].adjacent, a) +} + +console.log("if (typeof module === 'object') module.exports = data") +*/ diff --git a/tools/parse-layout.js b/tools/parse-layout.js index b382849..6f77adf 100644 --- a/tools/parse-layout.js +++ b/tools/parse-layout.js @@ -1,61 +1,30 @@ "use strict" -/* COMMON PARSING */ - const fs = require("fs") let points = {} -let circles = {} -let rects = {} +let boxes = {} let edges = {} +let mode, group, name, x, y, w, h, cx, cy, rx, ry, x2, y2 let labels = [] - -let mode, name, x, y, w, h, cx, cy, rx, ry, x2, y2 - -function array_insert(array, index, item) { - for (let i = array.length; i > index; --i) - array[i] = array[i - 1] - array[index] = item -} - -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) -} +let labels_uniq = {} function add_point(x, y) { - if (!(name in points)) - points[name] = [] - points[name].push({x,y}) -} - -function add_circle(cx, cy, rx, ry) { - if (!(name in circles)) - circles[name] = [] - circles[name].push({x:cx-rx,y:cy-ry,w:rx*2,h:ry*2}) + if (!(group in points)) + points[group] = [] + points[group].push({x,y,name}) } function add_rect(x, y, w, h) { - if (!(name in rects)) - rects[name] = [] - rects[name].push({x,y,w,h}) + if (!(group in boxes)) + boxes[group] = [] + boxes[group].push({x,y,w,h,name}) } function add_edge(x1, y1, x2, y2) { - if (!(name in edges)) - edges[name] = [] - edges[name].push({x1,y1,x2,y2}) + if (!(group in edges)) + edges[group] = [] + edges[group].push({x1,y1,x2,y2,name}) } function flush() { @@ -64,13 +33,14 @@ function flush() { } if (mode === 'rect') { add_rect(x, y, w, h) - add_point(x + w/2, y + h/2) + add_point( x + w/2, y + h/2 ) } if (mode === 'circle') { - add_circle(cx, cy, rx, ry) - add_point(cx, cy) + add_rect(cx - rx, cy - ry, rx * 2, ry * 2) + add_point( cx, cy ) } x = y = x2 = y2 = w = h = cx = cy = rx = ry = 0 + name = null } function parse_path_data(path) { @@ -184,18 +154,23 @@ for (let line of fs.readFileSync("tools/layout.svg", "utf-8").split("\n")) { else if (line.startsWith('ry="')) ry = (Number(line.split('"')[1])) else if (line.startsWith('inkscape:label="') && mode === "g") + group = line.split('"')[1] + else if (line.startsWith('inkscape:label="') && mode !== "g") name = line.split('"')[1] else if (line.startsWith('d="')) parse_path_data(line.split('"')[1].split(/[ ,]/)) if (line.includes("</tspan>")) { let name = line.replace(/^[^>]*>/, "").replace(/<\/tspan.*/, "") + if (name in labels_uniq) + console.log("DUPLICATE LABEL", name) labels.push({x, y, name}) + labels_uniq[name] = 1 } } flush() -function find_closest_label(x, y) { +function find_label(x, y, limit) { let nd = Infinity, nn = null for (let n of labels) { @@ -206,261 +181,35 @@ function find_closest_label(x, y) { } } - if (!nn) { - console.log("NOT FOUND", x, y) - return null - } - - if (nd > 50) { - console.log("NO LABEL", x, y, nd) + if (!nn || nd > limit) { + console.log("LABEL NOT FOUND", x, y) return null } return nn.name } -function find_closest_node(nodes, x, y) { - let nd = Infinity, nn = -1 - - for (let i = 0; i < nodes.length; ++i) { - let n = nodes[i] - let d = Math.hypot(n.x - x, n.y - y) - if (d < nd) { - nd = d - nn = i +for (let group in points) { + for (let item of points[group]) { + if (item.name === null) { + item.name = find_label(item.x, item.y, 72) } } - - if (nd > 100) - return -1 - - return nn } -function label_boxes(top) { - for (let key in top) { - console.log("BOX", key) - for (let item of top[key]) - item.name = find_closest_label(item.x + item.w/2, item.y + item.h/2) - } -} - -function label_points(top) { - for (let key in top) { - console.log("POINT", key) - for (let item of top[key]) - item.name = find_closest_label(item.x, item.y) - } -} - -label_boxes(rects) -label_boxes(circles) -label_points(points) - -function connect_edge_2way(node_list, edge_name) { - console.log("EDGE", edge_name) - for (let e of edges[edge_name]) { - let a = find_closest_node(node_list, e.x1, e.y1) - let b = find_closest_node(node_list, e.x2, e.y2) - if (a < 0 || b < 0) - console.log("CANNOT FIND EDGE", e, node_list[a], node_list[b]) - if (!node_list[a][edge_name]) - node_list[a][edge_name] = [] - if (!node_list[b][edge_name]) - node_list[b][edge_name] = [] - set_add(node_list[a][edge_name], b) - set_add(node_list[b][edge_name], a) - } -} - -function connect_edge_1way(a_list, b_list, edge_name, prop_name) { - console.log("EDGE", edge_name) - for (let e of edges[edge_name]) { - let a1 = find_closest_node(a_list, e.x1, e.y1) - let a2 = find_closest_node(a_list, e.x2, e.y2) - let b1 = find_closest_node(b_list, e.x1, e.y1) - let b2 = find_closest_node(b_list, e.x2, e.y2) - let a = a1 >= 0 ? a1 : a2 - let b = b1 >= 0 ? b1 : b2 - if (a < 0 || b < 0) - console.log("CANNOT FIND EDGE", e, a_list[a], b_list[b]) - if (!a_list[a][prop_name]) - a_list[a][prop_name] = [] - set_add(a_list[a][prop_name], b) - } -} - -/* WASHINGTON'S WAR */ - -function sort_alpha(list) { - list.sort((a,b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0) -} - -sort_alpha(points.fortress) -sort_alpha(points.winter_quarters) -sort_alpha(points.space) -points.colony.reverse() - -const data = {} - -data.spaces = [ ...points.fortress, ...points.winter_quarters, ...points.space ] -data.colonies = [ ...points.colony ] -data.seas = [ ...points.sea ] - -connect_edge_2way(data.spaces, "path") -connect_edge_2way(data.spaces, "wilderness") -connect_edge_1way(data.colonies, data.spaces, "colony", "spaces") -connect_edge_1way(data.seas, data.spaces, "sea", "spaces") - -console.log(JSON.stringify(data,0,4)) - -/* -function find_closest_node(list, x, y) { - let nd = Infinity, nn = null - - for (let n of list) { - let d = Math.hypot(n.x - x, n.y - y) - if (d < nd) { - nd = d - nn = n - } - } - - if (!nn) { - console.log("NOT FOUND", x, y) - return [ null, 0 ] - } - - return [ nn, nd ] -} - -function find_enclosing_rect(list, x, y) { - for (let [x1, y1, x2, y2] of list) { - if (x >= x1 && x <= x2) - if (y >= y1 && y <= y2) - return true - } - return false -} - - -function make_spaces(points) { - let spaces = [] - for (let [x,y] of points) { - let [ name, dist ] = find_closest_node(labels, x, y) - if (dist > 50) console.log("DISTANCE TOO FAR", x, y, dist) - - spaces.push({ name: name.name, x, y }) - } - return spaces -} - -let data = {} - -data.spaces = make_spaces([ ...points.fortress, ...points.winter_quarters, ...points.space ]) - -console.log(data) - -function find_closest_point(x, y) { - let nd = Infinity, nn = -1 - - for (let i = 0; i < points.length; ++i) { - let n = points[i] - let d = Math.hypot(n.x - x, n.y - y) - if (d < nd) { - nd = d - nn = i +for (let group in boxes) { + for (let item of boxes[group]) { + if (item.name === null) { + item.name = find_label(item.x+item.w/2, item.y+item.w/2, 72) } } - - return nn } -// FIND and label all points! -let all_labels = labels.slice() -let cities = [] -for (let key in points) { - for (let [x, y] of points[key]) { - let [ node, dist ] = find_closest_node(labels, x, y) - if (dist > 15) { - console.log("DISTANCE TOO FAR", key,x,y, "dist=" + dist, "name=" + node.name) - } - if (node) { - labels = labels.filter(x => x !== node) - let suit = "UNKNOWN" - - if (find_enclosing_rect(rects.$CLUBS, x, y)) - suit = CLUBS - else if (find_enclosing_rect(rects.$HEARTS, x, y)) - suit = HEARTS - else if (find_enclosing_rect(rects.$DIAMONDS, x, y)) - suit = DIAMONDS - else if (find_enclosing_rect(rects.$SPADES, x, y)) - suit = SPADES - else - console.log("NOT ASSIGNED SUIT", x, y) - - let country = "UNKNOWN" - if (find_enclosing_rect(rects.$Empire, x, y)) { - country = EMPIRE - } - else if (find_enclosing_rect(rects.$Austria, x, y)) { - country = AUSTRIA - } - else if (find_enclosing_rect(rects.$Hanover, x, y)) { - country = HANOVER - } - else if (find_enclosing_rect(rects.$Saxony, x, y)) { - country = SAXONY - } - else if (find_enclosing_rect(rects.$Sweden, x, y)) { - country = SWEDEN - } - else if (find_enclosing_rect(rects.$Poland, x, y)) { - country = POLAND - } - else if (find_enclosing_rect(rects.$Prussia, x, y)) { - country = PRUSSIA - } - - if (country === "UNKNOWN") - console.log("no country:", node) - - cities.push({ - name: node.name, - country, - suit, - type: key, - x: Math.round(x), - y: Math.round(y), - adjacent: [], - major_roads: [], - roads: [], - }) - } else { - let [ dupname, dupdist ] = find_closest_node(all_labels, x, y) - console.log("ALREADY USED", dupname, dupdist, x, y) - } +for (let group in edges) { + for (let item of edges[group]) { + item.name1 = find_label(item.x1, item.y1, 72) + item.name2 = find_label(item.x2, item.y2, 72) } } -for (let e of edges.major_road) { - let a = find_closest_point(e.x1, e.y1) - let b = find_closest_point(e.x2, e.y2) - set_add(cities[a].major_roads, b) - set_add(cities[b].major_roads, a) - set_add(cities[a].adjacent, b) - set_add(cities[b].adjacent, a) -} - -for (let e of edges.road) { - let a = find_closest_point(e.x1, e.y1) - let b = find_closest_point(e.x2, e.y2) - set_add(cities[a].roads, b) - set_add(cities[b].roads, a) - set_add(cities[a].adjacent, b) - set_add(cities[b].adjacent, a) -} - -console.log("if (typeof module === 'object') module.exports = data") -*/ +fs.writeFileSync("tools/layout.json", JSON.stringify({boxes,points,edges,labels}, null, 4)) diff --git a/tools/process-layout.js b/tools/process-layout.js new file mode 100644 index 0000000..6de0844 --- /dev/null +++ b/tools/process-layout.js @@ -0,0 +1,164 @@ +"use strict" + +const fs = require("fs") + +let layout = JSON.parse(fs.readFileSync("tools/layout.json", "utf-8")) + +function array_insert(array, index, item) { + for (let i = array.length; i > index; --i) + array[i] = array[i - 1] + array[index] = item +} + +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 find_enclosing_rect(list, x, y) { + for (let {x1, y1, x2, y2} of list) { + if (x >= x1 && x <= x2) + if (y >= y1 && y <= y2) + return true + } + return false +} + +const SEAS = [ "Sea1", "Sea2", "Sea3", "Sea4", "Sea5", "Sea6", "Sea7" ] +const COLONIES = [ "Canada", "NH", "NY", "MA", "CT", "RI", "PA", "NJ", "MD", "DE", "VA", "NC", "SC", "GA", ] + +let seas = SEAS.map(x => []) +let colonies = COLONIES.map(x => []) + +let spaces = [] +let space_index = {} + +function add_space(type, name, x, y) { + let id = spaces.length + spaces.push({ + name, + type, + adjacent: [], + path: [], + wilderness: [], + port: -1, + colony: -1, + x: Math.round(x), + y: Math.round(y), + }) +} + +function add_edge(type, a, b) { + let ax = space_index[a] + let bx = space_index[b] + set_add(spaces[ax][type], bx) + set_add(spaces[bx][type], ax) + set_add(spaces[ax].adjacent, bx) + set_add(spaces[bx].adjacent, ax) +} + +function add_oneway(type, a, b, list2, set2) { + let ax = space_index[a] + let bx = space_index[b] + if (ax !== undefined) { + let k = list2.indexOf(b) + spaces[ax][type] = k + set_add(set2[k], ax) + } + if (bx !== undefined) { + let k = list2.indexOf(a) + spaces[bx][type] = k + set_add(set2[k], bx) + } +} + +for (let item of layout.points.fortified_port) + add_space("fortified_port", item.name, item.x, item.y) +for (let item of layout.points.winter_quarters) + add_space("winter_quarters", item.name, item.x, item.y) +for (let item of layout.points.regular) + add_space("regular", item.name, item.x, item.y) +for (let item of layout.points.box) + add_space("box", item.name, item.x, item.y) + +const TYPE_SORT = [ "fortified_port", "winter_quarters", "regular", "box" ] + +spaces.sort((a,b) => { + let at = TYPE_SORT.indexOf(a.type) + let bt = TYPE_SORT.indexOf(b.type) + if (at < bt) return -1 + if (at > bt) return 1 + if (a.name < b.name) return -1 + if (a.name > b.name) return 1 + return b.y - a.y +}) + +for (let i = 0; i < spaces.length; ++i) + space_index[spaces[i].name] = i + +for (let item of layout.edges.sea) + add_oneway("port", item.name1, item.name2, SEAS, seas) +for (let item of layout.edges.colony) + add_oneway("colony", item.name1, item.name2, COLONIES, colonies) + +for (let item of layout.edges.path) + add_edge("path", item.name1, item.name2) +for (let item of layout.edges.wilderness) + add_edge("wilderness", item.name1, item.name2) + + +function map_point(group, name) { + let item = layout.points[group].find(item => item.name === name) + return [ item.x, item.y ].map(Math.round) +} + +function map_box(group, name) { + let item = layout.boxes[group].find(item => item.name === name) + return [ item.x, item.y, item.w, item.h ].map(Math.round) +} + +function map_point_list(group, names) { + return names.map(n => map_point(group, n)) +} + +function map_box_list(group, names) { + return names.map(n => map_box(group, n)) +} + +function map_box_dict(list) { + let out = {} + for (let item of list) + out[item.name] = [ item.x, item.y, item.w, item.h ].map(Math.round) + return out +} + +function map_point_dict(list) { + let out = {} + for (let item of list) + out[item.name] = [ item.x, item.y ].map(Math.round) + return out +} + +let boxes = { + sea: map_box_list("sea", SEAS), + colony: map_point_list("colony", COLONIES), + french_alliance_track: map_point_dict(layout.points["French Alliance Track"]), + turn: map_point_dict(layout.points["Game Turn"]), + card_box: map_point_dict(layout.points["Card Boxes"]), + box: map_box_dict(layout.boxes.box), +} + +let data = { layout: boxes, spaces, space_index, colonies, seas } + +fs.writeFileSync("tools/data.json", JSON.stringify(data)) diff --git a/tools/rotate_year.sh b/tools/rotate_year.sh new file mode 100644 index 0000000..96a6c2b --- /dev/null +++ b/tools/rotate_year.sh @@ -0,0 +1,8 @@ +for DIR in cards75 cards100 cards150 cards200 +do + convert -rotate 90 $DIR/card_7.png $DIR/card_7_rot.png + convert -rotate 90 $DIR/card_8.png $DIR/card_8_rot.png + convert -rotate 90 $DIR/card_9.png $DIR/card_9_rot.png + convert -rotate 90 $DIR/card_10.png $DIR/card_10_rot.png + convert -rotate 90 $DIR/card_11.png $DIR/card_11_rot.png +done |