diff options
-rw-r--r-- | data.js | 2 | ||||
-rw-r--r-- | rules.js | 530 | ||||
-rw-r--r-- | tools/gendata.js | 3 |
3 files changed, 422 insertions, 113 deletions
@@ -1,2 +1,2 @@ -const data = {"first_space":0,"first_pop":1,"first_city":0,"last_city":10,"first_dept":11,"last_pop":22,"last_dept":26,"first_foreign":27,"last_foreign":31,"first_loc":32,"last_loc":49,"last_space":49,"coastal_spaces":[11,12,13,17],"card_name":[null,"1st Division","Ospina & Mora","Tapias","Caño Limón - Coveñas","Occidental & Ecopetrol","Oil Spill","7th Special Forces","Fuerza Aérea Colombiana","High Mountain Battalions","Blackhawks","National Defense & Security Council","Plan Colombia","Plan Meteoro","Tres Esquinas","War Tax","Coffee Prices","Madrid Donors","NSPD-18","General Offensive","Mono Jojoy","Raúl Reyes","Alfonso Cano","DoD Contractors","Operación Jaque","Ejército de Liberación Nacional","Gramaje","Misil Antiaéreo","Hugo Chávez","Kill Zone","Peace Commission","Betancourt","Secuestrados","Sucumbíos","Airdropped AKs","Crop Substitution","Zona de Convivencia","Former Military","National Coordination Center","Soldados Campesinos","Demobilization","Mancuso","Senado & Cámara","Calima Front","Colombia Nueva","Los Derechos Humanos","Limpieza","Pinto & del Rosario","Unión Sindical Obrera","Bloques","Carabineros","Pipeline Repairs","Castaño","Criminal Air Force","Deserters & Defectors","DEA Agents","Drogas La Rebaja","Op Millennium","General Serrano","Salcedo","The Chess Player","Air Bridge","Amazonía","Narco-War","Cocaine Labs","Poppies","Tingo María","Mexican Traffickers","Narco-Subs","Riverines & Fast Boats","Ayahuasca Tourism","Darién","Sicarios","Propaganda!","Propaganda!","Propaganda!","Propaganda!"],"card_order":[null,[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,3,2],[0,1,3,2],[0,1,3,2],[0,2,1,3],[0,2,1,3],[0,2,1,3],[0,2,3,1],[0,2,3,1],[0,2,3,1],[0,3,1,2],[0,3,1,2],[0,3,1,2],[0,3,2,1],[0,3,2,1],[0,3,2,1],[1,0,2,3],[1,0,2,3],[1,0,2,3],[1,0,3,2],[1,0,3,2],[1,0,3,2],[1,2,0,3],[1,2,0,3],[1,2,0,3],[1,2,3,0],[1,2,3,0],[1,2,3,0],[1,3,0,2],[1,3,0,2],[1,3,0,2],[1,3,2,0],[1,3,2,0],[1,3,2,0],[2,0,1,3],[2,0,1,3],[2,0,1,3],[2,0,3,1],[2,0,3,1],[2,0,3,1],[2,1,0,3],[2,1,0,3],[2,1,0,3],[2,1,3,0],[2,1,3,0],[2,1,3,0],[2,3,0,1],[2,3,0,1],[2,3,0,1],[2,3,1,0],[2,3,1,0],[2,3,1,0],[3,0,1,2],[3,0,1,2],[3,0,1,2],[3,0,2,1],[3,0,2,1],[3,0,2,1],[3,1,0,2],[3,1,0,2],[3,1,0,2],[3,1,2,0],[3,1,2,0],[3,1,2,0],[3,2,0,1],[3,2,0,1],[3,2,0,1],[3,2,1,0],[3,2,1,0],[3,2,1,0],[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,2,3]],"space_name":["Bogotá","Cali","Medellín","Bucaramanga","Ibagué","Santa Marta","Cartagena","Cúcuta","Neiva","Pasto","Sincelejo","Atlántico","Chocó","Nariño","Meta West","Guaviare","Putumayo","Cesar","Antioquia","Santander","Huila","Arauca","Meta East","Vichada","Guainía","Vaupés","Amazonas","Panamá","Ecuador","Brasil","Perú","Venezuela","Santa Marta / Cartagena","Cali / Pasto","Neiva / Pasto","Bogotá / San José","Pasto / Tumaco","Cali / Buenaventura","Cartagena / Sincelejo","Sincelejo / Medellín","Medellín / Ibagué","Ibagué / Cali","Bucaramanga / Ibagué / Bogotá","Bogotá / Neiva","Cúcuta / Arauca","Bogotá / Yopal","Santa Marta / Ayacucho","Bucaramanga / Ayacucho","Sincelejo / Ayacucho","Cúcuta / Ayacucho"],"spaces":[{"type":"city","id":"Bogota","pop":8,"adjacent":[14,19,20,22,35,42,43,45]},{"type":"city","id":"Cali","pop":3,"adjacent":[12,13,20,33,37,41]},{"type":"city","id":"Medellin","pop":3,"adjacent":[12,18,39,40]},{"type":"city","id":"Bucaramanga","pop":2,"adjacent":[18,19,42,47]},{"type":"city","id":"Ibague","pop":2,"adjacent":[12,18,20,40,41,42]},{"type":"city","id":"Santa Marta","pop":2,"adjacent":[11,17,32,46]},{"type":"city","id":"Cartagena","pop":1,"adjacent":[11,32,38]},{"type":"city","id":"Cucuta","pop":1,"adjacent":[19,31,44,49]},{"type":"city","id":"Neiva","pop":1,"adjacent":[14,16,20,34,43]},{"type":"city","id":"Pasto","pop":1,"adjacent":[13,16,20,28,33,34,36]},{"type":"city","id":"Sincelejo","pop":1,"adjacent":[11,12,18,38,39,48]},{"type":"forest","id":"Atlantico","pop":1,"adjacent":[5,6,10,17,18,19,32,38,46,47,48,49]},{"type":"forest","id":"Choco","pop":1,"adjacent":[1,2,4,10,13,18,20,27,37,39,40,41]},{"type":"forest","id":"Narino","pop":1,"adjacent":[1,9,12,20,28,33,36,37]},{"type":"forest","id":"Meta West","pop":1,"adjacent":[0,8,15,16,20,22,35,43]},{"type":"forest","id":"Guaviare","pop":1,"adjacent":[14,16,22,23,24,25,35]},{"type":"forest","id":"Putumayo","pop":1,"adjacent":[8,9,14,15,20,25,26,28,30,34]},{"type":"mountain","id":"Cesar","pop":1,"adjacent":[5,11,18,19,31,46,47,48,49]},{"type":"mountain","id":"Antioquia","pop":2,"adjacent":[2,3,4,10,11,12,17,19,20,39,40,42,46,47,48,49]},{"type":"mountain","id":"Santander","pop":2,"adjacent":[0,3,7,11,17,18,20,21,22,31,42,44,45,46,47,48,49]},{"type":"mountain","id":"Huila","pop":2,"adjacent":[0,1,4,8,9,12,13,14,16,18,19,33,34,41,42,43]},{"type":"grassland","id":"Arauca","pop":1,"adjacent":[19,22,23,31,44,45]},{"type":"grassland","id":"Meta East","pop":1,"adjacent":[0,14,15,19,21,23,35,45]},{"type":"grassland","id":"Vichada","pop":0,"adjacent":[15,21,22,24,31]},{"type":"forest","id":"Guainia","pop":0,"adjacent":[15,23,25,29,31]},{"type":"forest","id":"Vaupes","pop":0,"adjacent":[15,16,24,26,29]},{"type":"forest","id":"Amazonas","pop":0,"adjacent":[16,25,29,30]},{"type":"foreign","id":"Panama","pop":0,"adjacent":[12]},{"type":"foreign","id":"Ecuador","pop":0,"adjacent":[9,13,16,36]},{"type":"foreign","id":"Brasil","pop":0,"adjacent":[24,25,26]},{"type":"foreign","id":"Peru","pop":0,"adjacent":[16,26]},{"type":"foreign","id":"Venezuela","pop":0,"adjacent":[7,17,19,21,23,24,44,49]},{"type":"road","id":"Santa Marta / Cartagena","econ":1,"adjacent":[5,6,11]},{"type":"road","id":"Cali / Pasto","econ":1,"adjacent":[1,9,13,20]},{"type":"road","id":"Neiva / Pasto","econ":1,"adjacent":[8,9,16,20]},{"type":"road","id":"Bogota / San Jose","econ":1,"adjacent":[0,14,15,22]},{"type":"road","id":"Pasto / Tumaco","econ":1,"adjacent":[9,13,28]},{"type":"pipeline","id":"Cali / Buenaventura","econ":1,"adjacent":[1,12,13]},{"type":"pipeline","id":"Cartagena / Sincelejo","econ":1,"adjacent":[6,10,11]},{"type":"pipeline","id":"Sincelejo / Medellin","econ":2,"adjacent":[2,10,12,18]},{"type":"pipeline","id":"Medellin / Ibague","econ":1,"adjacent":[2,4,12,18]},{"type":"pipeline","id":"Ibague / Cali","econ":1,"adjacent":[1,4,12,20]},{"type":"pipeline","id":"Bucaramanga / Ibague / Bogota","econ":2,"adjacent":[0,3,4,18,19,20]},{"type":"pipeline","id":"Bogota / Neiva","econ":2,"adjacent":[0,8,14,20]},{"type":"pipeline","id":"Cucuta / Arauca","econ":3,"adjacent":[7,19,21,21,31]},{"type":"pipeline","id":"Bogota / Yopal","econ":2,"adjacent":[0,19,21,22]},{"type":"pipeline","id":"Santa Marta / Ayacucho","econ":2,"adjacent":[5,11,17,18,19,47,48,49]},{"type":"pipeline","id":"Bucaramanga / Ayacucho","econ":2,"adjacent":[3,11,17,18,19,46,48,49]},{"type":"pipeline","id":"Sincelejo / Ayacucho","econ":3,"adjacent":[10,11,17,18,19,46,47,49]},{"type":"pipeline","id":"Cucuta / Ayacucho","econ":3,"adjacent":[7,11,17,18,19,31,46,47,48]}],"adjacent_patrol":[[35,42,43,45],[33,37,41],[39,40],[42,47],[40,41,42],[32,46],[32,38],[44,49],[34,43],[33,34,36],[38,39,48],[5,6,10,32,38,46,47,48,49],[1,2,4,10,37,39,40,41],[1,9,33,36,37],[0,8,35,43],[35],[8,9,34],[5,46,47,48,49],[2,3,4,10,39,40,42,46,47,48,49],[0,3,7,42,44,45,46,47,48,49],[0,1,4,8,9,33,34,41,42,43],[44,45],[0,35,45],[],[],[],[],[],[9,36],[],[],[7,44,49],[5,6],[1,9],[8,9],[0],[9],[1],[6,10],[2,10],[2,4],[1,4],[0,3,4],[0,8],[7],[0],[5,47,48,49],[3,46,48,49],[10,46,47,49],[7,46,47,48]],"first_piece":[[0,3,3,33,0],[63,72],[102,108],[126,141]],"last_piece":[[2,2,32,62,-1],[71,101],[107,125],[140,152]]} +const data = {"first_space":0,"first_pop":0,"first_city":0,"last_city":10,"first_dept":11,"last_pop":22,"last_dept":26,"first_foreign":27,"last_foreign":31,"first_loc":32,"last_loc":49,"last_space":49,"coastal_spaces":[11,12,13,17],"card_name":[null,"1st Division","Ospina & Mora","Tapias","Caño Limón - Coveñas","Occidental & Ecopetrol","Oil Spill","7th Special Forces","Fuerza Aérea Colombiana","High Mountain Battalions","Blackhawks","National Defense & Security Council","Plan Colombia","Plan Meteoro","Tres Esquinas","War Tax","Coffee Prices","Madrid Donors","NSPD-18","General Offensive","Mono Jojoy","Raúl Reyes","Alfonso Cano","DoD Contractors","Operación Jaque","Ejército de Liberación Nacional","Gramaje","Misil Antiaéreo","Hugo Chávez","Kill Zone","Peace Commission","Betancourt","Secuestrados","Sucumbíos","Airdropped AKs","Crop Substitution","Zona de Convivencia","Former Military","National Coordination Center","Soldados Campesinos","Demobilization","Mancuso","Senado & Cámara","Calima Front","Colombia Nueva","Los Derechos Humanos","Limpieza","Pinto & del Rosario","Unión Sindical Obrera","Bloques","Carabineros","Pipeline Repairs","Castaño","Criminal Air Force","Deserters & Defectors","DEA Agents","Drogas La Rebaja","Op Millennium","General Serrano","Salcedo","The Chess Player","Air Bridge","Amazonía","Narco-War","Cocaine Labs","Poppies","Tingo María","Mexican Traffickers","Narco-Subs","Riverines & Fast Boats","Ayahuasca Tourism","Darién","Sicarios","Propaganda!","Propaganda!","Propaganda!","Propaganda!"],"card_order":[null,[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,3,2],[0,1,3,2],[0,1,3,2],[0,2,1,3],[0,2,1,3],[0,2,1,3],[0,2,3,1],[0,2,3,1],[0,2,3,1],[0,3,1,2],[0,3,1,2],[0,3,1,2],[0,3,2,1],[0,3,2,1],[0,3,2,1],[1,0,2,3],[1,0,2,3],[1,0,2,3],[1,0,3,2],[1,0,3,2],[1,0,3,2],[1,2,0,3],[1,2,0,3],[1,2,0,3],[1,2,3,0],[1,2,3,0],[1,2,3,0],[1,3,0,2],[1,3,0,2],[1,3,0,2],[1,3,2,0],[1,3,2,0],[1,3,2,0],[2,0,1,3],[2,0,1,3],[2,0,1,3],[2,0,3,1],[2,0,3,1],[2,0,3,1],[2,1,0,3],[2,1,0,3],[2,1,0,3],[2,1,3,0],[2,1,3,0],[2,1,3,0],[2,3,0,1],[2,3,0,1],[2,3,0,1],[2,3,1,0],[2,3,1,0],[2,3,1,0],[3,0,1,2],[3,0,1,2],[3,0,1,2],[3,0,2,1],[3,0,2,1],[3,0,2,1],[3,1,0,2],[3,1,0,2],[3,1,0,2],[3,1,2,0],[3,1,2,0],[3,1,2,0],[3,2,0,1],[3,2,0,1],[3,2,0,1],[3,2,1,0],[3,2,1,0],[3,2,1,0],[0,1,2,3],[0,1,2,3],[0,1,2,3],[0,1,2,3]],"space_name":["Bogotá","Cali","Medellín","Bucaramanga","Ibagué","Santa Marta","Cartagena","Cúcuta","Neiva","Pasto","Sincelejo","Atlántico","Chocó","Nariño","Meta West","Guaviare","Putumayo","Cesar","Antioquia","Santander","Huila","Arauca","Meta East","Vichada","Guainía","Vaupés","Amazonas","Panamá","Ecuador","Brasil","Perú","Venezuela","Santa Marta / Cartagena","Cali / Pasto","Neiva / Pasto","Bogotá / San José","Pasto / Tumaco","Cali / Buenaventura","Cartagena / Sincelejo","Sincelejo / Medellín","Medellín / Ibagué","Ibagué / Cali","Bucaramanga / Ibagué / Bogotá","Bogotá / Neiva","Cúcuta / Arauca","Bogotá / Yopal","Santa Marta / Ayacucho","Bucaramanga / Ayacucho","Sincelejo / Ayacucho","Cúcuta / Ayacucho"],"spaces":[{"type":"city","id":"Bogota","pop":8,"adjacent":[14,19,20,22,35,42,43,45]},{"type":"city","id":"Cali","pop":3,"adjacent":[12,13,20,33,37,41]},{"type":"city","id":"Medellin","pop":3,"adjacent":[12,18,39,40]},{"type":"city","id":"Bucaramanga","pop":2,"adjacent":[18,19,42,47]},{"type":"city","id":"Ibague","pop":2,"adjacent":[12,18,20,40,41,42]},{"type":"city","id":"Santa Marta","pop":2,"adjacent":[11,17,32,46]},{"type":"city","id":"Cartagena","pop":1,"adjacent":[11,32,38]},{"type":"city","id":"Cucuta","pop":1,"adjacent":[19,31,44,49]},{"type":"city","id":"Neiva","pop":1,"adjacent":[14,16,20,34,43]},{"type":"city","id":"Pasto","pop":1,"adjacent":[13,16,20,28,33,34,36]},{"type":"city","id":"Sincelejo","pop":1,"adjacent":[11,12,18,38,39,48]},{"type":"forest","id":"Atlantico","pop":1,"adjacent":[5,6,10,17,18,19,32,38,46,47,48,49]},{"type":"forest","id":"Choco","pop":1,"adjacent":[1,2,4,10,13,18,20,27,37,39,40,41]},{"type":"forest","id":"Narino","pop":1,"adjacent":[1,9,12,20,28,33,36,37]},{"type":"forest","id":"Meta West","pop":1,"adjacent":[0,8,15,16,20,22,35,43]},{"type":"forest","id":"Guaviare","pop":1,"adjacent":[14,16,22,23,24,25,35]},{"type":"forest","id":"Putumayo","pop":1,"adjacent":[8,9,14,15,20,25,26,28,30,34]},{"type":"mountain","id":"Cesar","pop":1,"adjacent":[5,11,18,19,31,46,47,48,49]},{"type":"mountain","id":"Antioquia","pop":2,"adjacent":[2,3,4,10,11,12,17,19,20,39,40,42,46,47,48,49]},{"type":"mountain","id":"Santander","pop":2,"adjacent":[0,3,7,11,17,18,20,21,22,31,42,44,45,46,47,48,49]},{"type":"mountain","id":"Huila","pop":2,"adjacent":[0,1,4,8,9,12,13,14,16,18,19,33,34,41,42,43]},{"type":"grassland","id":"Arauca","pop":1,"adjacent":[19,22,23,31,44,45]},{"type":"grassland","id":"Meta East","pop":1,"adjacent":[0,14,15,19,21,23,35,45]},{"type":"grassland","id":"Vichada","pop":0,"adjacent":[15,21,22,24,31]},{"type":"forest","id":"Guainia","pop":0,"adjacent":[15,23,25,29,31]},{"type":"forest","id":"Vaupes","pop":0,"adjacent":[15,16,24,26,29]},{"type":"forest","id":"Amazonas","pop":0,"adjacent":[16,25,29,30]},{"type":"foreign","id":"Panama","pop":0,"adjacent":[12]},{"type":"foreign","id":"Ecuador","pop":0,"adjacent":[9,13,16,36]},{"type":"foreign","id":"Brasil","pop":0,"adjacent":[24,25,26]},{"type":"foreign","id":"Peru","pop":0,"adjacent":[16,26]},{"type":"foreign","id":"Venezuela","pop":0,"adjacent":[7,17,19,21,23,24,44,49]},{"type":"road","id":"Santa Marta / Cartagena","econ":1,"adjacent":[5,6,11]},{"type":"road","id":"Cali / Pasto","econ":1,"adjacent":[1,9,13,20]},{"type":"road","id":"Neiva / Pasto","econ":1,"adjacent":[8,9,16,20]},{"type":"road","id":"Bogota / San Jose","econ":1,"adjacent":[0,14,15,22]},{"type":"road","id":"Pasto / Tumaco","econ":1,"adjacent":[9,13,28]},{"type":"pipeline","id":"Cali / Buenaventura","econ":1,"adjacent":[1,12,13]},{"type":"pipeline","id":"Cartagena / Sincelejo","econ":1,"adjacent":[6,10,11]},{"type":"pipeline","id":"Sincelejo / Medellin","econ":2,"adjacent":[2,10,12,18]},{"type":"pipeline","id":"Medellin / Ibague","econ":1,"adjacent":[2,4,12,18]},{"type":"pipeline","id":"Ibague / Cali","econ":1,"adjacent":[1,4,12,20]},{"type":"pipeline","id":"Bucaramanga / Ibague / Bogota","econ":2,"adjacent":[0,3,4,18,19,20]},{"type":"pipeline","id":"Bogota / Neiva","econ":2,"adjacent":[0,8,14,20]},{"type":"pipeline","id":"Cucuta / Arauca","econ":3,"adjacent":[7,19,21,21,31]},{"type":"pipeline","id":"Bogota / Yopal","econ":2,"adjacent":[0,19,21,22]},{"type":"pipeline","id":"Santa Marta / Ayacucho","econ":2,"adjacent":[5,11,17,18,19,47,48,49]},{"type":"pipeline","id":"Bucaramanga / Ayacucho","econ":2,"adjacent":[3,11,17,18,19,46,48,49]},{"type":"pipeline","id":"Sincelejo / Ayacucho","econ":3,"adjacent":[10,11,17,18,19,46,47,49]},{"type":"pipeline","id":"Cucuta / Ayacucho","econ":3,"adjacent":[7,11,17,18,19,31,46,47,48]}],"adjacent_patrol":[[35,42,43,45],[33,37,41],[39,40],[42,47],[40,41,42],[32,46],[32,38],[44,49],[34,43],[33,34,36],[38,39,48],[5,6,10,32,38,46,47,48,49],[1,2,4,10,37,39,40,41],[1,9,33,36,37],[0,8,35,43],[35],[8,9,34],[5,46,47,48,49],[2,3,4,10,39,40,42,46,47,48,49],[0,3,7,42,44,45,46,47,48,49],[0,1,4,8,9,33,34,41,42,43],[44,45],[0,35,45],[],[],[],[],[],[9,36],[],[],[7,44,49],[5,6],[1,9],[8,9],[0],[9],[1],[6,10],[2,10],[2,4],[1,4],[0,3,4],[0,8],[7],[0],[5,47,48,49],[3,46,48,49],[10,46,47,49],[7,46,47,48]],"first_piece":[[0,3,3,33,0],[63,72],[102,108],[126,141]],"last_piece":[[2,2,32,62,-1],[71,101],[107,125],[140,152]]} if (typeof module !== 'undefined') module.exports = data @@ -4,6 +4,8 @@ // TODO: auto-next at end of Special Activity / operation space ? // TODO: for (s = ... last_space) to for_each_space (including/excluding foreign countries depending on events) +// TODO: how granular undo (one at start of each space, each step, or each piece?) +// TODO: resume_...activity - end automatically when no more possible let states = {} let game = null @@ -409,6 +411,53 @@ function count_pieces(s, faction, type) { return n } +function is_base(p) { + if (p >= first_piece[GOVT][BASE] && p <= last_piece[GOVT][BASE]) + return true + if (p >= first_piece[FARC][BASE] && p <= last_piece[FARC][BASE]) + return true + if (p >= first_piece[AUC][BASE] && p <= last_piece[AUC][BASE]) + return true + if (p >= first_piece[CARTELS][BASE] && p <= last_piece[CARTELS][BASE]) + return true + return false +} + +function target_faction(p) { + if (p >= first_piece[GOVT][TROOPS] && p <= last_piece[GOVT][TROOPS]) + return 1 << GOVT + if (p >= first_piece[GOVT][POLICE] && p <= last_piece[GOVT][POLICE]) + return 1 << GOVT + if (p >= first_piece[GOVT][BASE] && p <= last_piece[GOVT][BASE]) + return 1 << GOVT + if (p >= first_piece[FARC][GUERRILLA] && p <= last_piece[FARC][GUERRILLA]) + return 1 << FARC + if (p >= first_piece[FARC][BASE] && p <= last_piece[FARC][BASE]) + return 1 << FARC + if (p >= first_piece[AUC][GUERRILLA] && p <= last_piece[AUC][GUERRILLA]) + return 1 << AUC + if (p >= first_piece[AUC][BASE] && p <= last_piece[AUC][BASE]) + return 1 << AUC + if (p >= first_piece[CARTELS][GUERRILLA] && p <= last_piece[CARTELS][GUERRILLA]) + return 1 << CARTELS + if (p >= first_piece[CARTELS][BASE] && p <= last_piece[CARTELS][BASE]) + return 1 << CARTELS + return 0 +} + +function has_targeted_faction(faction) { + let bit = 1 << faction + return game.op.targeted & bit +} + +function did_maximum_damage(targeted) { + if (view.actions.piece) + for (let p of view.actions.piece) + if (targeted & target_faction(p)) + return false + return true +} + function has_piece(s, faction, type) { let first = first_piece[faction][type] let last = last_piece[faction][type] @@ -703,7 +752,7 @@ function gen_active_guerrillas(s, faction) { }) } -function gen_remove_piece(s, faction) { +function gen_attack_piece(s, faction) { if (faction === GOVT) { gen_piece_in_space(s, GOVT, TROOPS) gen_piece_in_space(s, GOVT, POLICE) @@ -923,6 +972,7 @@ function goto_operation(free, limited, special) { free, limited, spaces: [], + targeted: 0, pieces: 0, count: 0, } @@ -1073,7 +1123,7 @@ function select_op_space(s, cost) { // OPERATION: TRAIN -states.train = operation({ +states.train = { prompt() { view.prompt = "Train: Select spaces to place cubes." @@ -1108,9 +1158,9 @@ states.train = operation({ push_undo() game.state = "train_base_or_civic" } -}) +} -states.train_place = operation({ +states.train_place = { prompt() { view.prompt = `Train in ${space_name[game.op.where]}: Place up to ${game.op.count} cubes.` view.where = game.op.where @@ -1133,17 +1183,12 @@ states.train_place = operation({ game.op.count = 0 game.state = "train" }, -}) +} -states.train_base_or_civic = operation({ +states.train_base_or_civic = { prompt() { view.prompt = "Train: Build Base or buy Civic Action in one selected space?" - if (game.sa) { - view.actions.air_lift = 1 - view.actions.eradicate = 1 - } - view.actions.base = 1 if (game.resources[game.current] >= 3) view.actions.civic = 1 @@ -1163,15 +1208,10 @@ states.train_base_or_civic = operation({ game.op.where = -1 }, end_operation, -}) +} -states.train_base = operation({ +states.train_base = { prompt() { - if (game.sa) { - view.actions.air_lift = 1 - view.actions.eradicate = 1 - } - if (game.op.where < 0) { view.prompt = `Train: Replace 3 cubes with a Base.` for (let s of game.op.spaces) { @@ -1207,15 +1247,10 @@ states.train_base = operation({ update_control() }, end_operation, -}) +} -states.train_civic = operation({ +states.train_civic = { prompt() { - if (game.sa) { - view.actions.air_lift = 1 - view.actions.eradicate = 1 - } - let res = game.resources[game.current] if (game.op.where < 0) { view.prompt = `Train: Buy Civic Action.` @@ -1244,11 +1279,11 @@ states.train_civic = operation({ game.support[game.op.where] += 1 }, end_operation, -}) +} // OPERATION: PATROL -states.patrol_pay = operation({ +states.patrol_pay = { prompt() { view.prompt = "Patrol: Pay 3 resources." gen_action("resources", GOVT) @@ -1257,7 +1292,7 @@ states.patrol_pay = operation({ game.resources[GOVT] -= 3 goto_patrol() } -}) +} function goto_patrol() { if (game.op.limited) @@ -1268,7 +1303,7 @@ function goto_patrol() { game.op.pieces = [] } -states.patrol_limop = operation({ +states.patrol_limop = { prompt() { view.prompt = "Patrol: Select destination City or LoC." @@ -1290,7 +1325,7 @@ states.patrol_limop = operation({ game.op.where = s game.state = "patrol_limop_move" }, -}) +} function gen_patrol_move_limop(start) { let queue = [ start ] @@ -1317,7 +1352,7 @@ function gen_patrol_move_limop(start) { } } -states.patrol_limop_move = operation({ +states.patrol_limop_move = { prompt() { view.prompt = `Patrol: Move cubes to ${space_name[game.op.where]}.` view.where = game.op.where @@ -1334,7 +1369,7 @@ states.patrol_limop_move = operation({ update_control() }, next: goto_patrol_activate, -}) +} function gen_patrol_move(start) { let queue = [ start ] @@ -1358,9 +1393,9 @@ function gen_patrol_move(start) { } } -states.patrol = operation({ +states.patrol = { prompt() { - view.prompt = "Patrol: ..." + view.prompt = "Patrol: Move cubes into or along LoCs and Cities." view.who = game.op.who gen_any_govt_special() @@ -1402,7 +1437,7 @@ states.patrol = operation({ update_control() }, next: goto_patrol_activate, -}) +} function goto_patrol_activate() { push_undo() @@ -1412,7 +1447,7 @@ function goto_patrol_activate() { game.op.count[s - first_loc] = count_cubes(s) } -states.patrol_activate = operation({ +states.patrol_activate = { prompt() { view.prompt = "Patrol: Activate 1 Guerrilla for each cube in LoCs." @@ -1428,21 +1463,26 @@ states.patrol_activate = operation({ } } - view.actions.next = 1 + if (did_maximum_damage(game.op.targeted)) + view.actions.next = 1 + else + view.actions.next = 0 }, piece(p) { push_undo() let s = game.pieces[p] + game.op.targeted |= target_faction(p) set_active(p) game.op.count[s - first_loc]-- }, next() { push_undo() + // TODO: is possible? game.state = "patrol_assault" }, -}) +} -states.patrol_assault = operation({ +states.patrol_assault = { prompt() { view.prompt = "Patrol: Assault in one LoC?" @@ -1469,17 +1509,15 @@ states.patrol_assault = operation({ game.op.count = count_cubes(s) }, end_operation, -}) +} -states.patrol_assault_space = operation({ +states.patrol_assault_space = { prompt() { view.prompt = `Patrol: Remove ${game.op.count} enemy pieces in ${space_name[game.op.where]}.` view.where = game.op.where gen_any_govt_special() - // TODO: maximum damage among targeted factions - if (game.op.count > 0) { gen_exposed_piece(game.op.where, FARC) gen_exposed_piece(game.op.where, AUC) @@ -1489,15 +1527,19 @@ states.patrol_assault_space = operation({ if (!view.actions.piece) view.prompt = "Patrol: All done." - view.actions.end_operation = 1 + if (did_maximum_damage(game.op.targeted)) + view.actions.end_operation = 1 + else + view.actions.end_operation = 0 }, piece(p) { push_undo() + game.op.targeted |= target_faction(p) remove_piece(p) update_control() }, end_operation, -}) +} // OPERATION: SWEEP @@ -1530,7 +1572,7 @@ function gen_sweep_move(here) { } } -states.sweep = operation({ +states.sweep = { prompt() { view.prompt = "Sweep: Select Cities and Departments." @@ -1563,9 +1605,9 @@ states.sweep = operation({ game.op.where = s }, end_operation, -}) +} -states.sweep_move = operation({ +states.sweep_move = { prompt() { view.prompt = `Sweep: Move Troops into ${space_name[game.op.where]}.` view.where = game.op.where @@ -1588,25 +1630,27 @@ states.sweep_move = operation({ if (is_forest(game.op.where)) game.op.count >>= 1 }, -}) +} -states.sweep_activate = operation({ +states.sweep_activate = { prompt() { view.prompt = `Sweep: Activate ${game.op.count} Guerrillas in ${space_name[game.op.where]}.` view.where = game.op.where gen_any_govt_special() - // TODO: maximum damage among targeted factions - gen_underground_guerrillas(game.op.where, FARC) gen_underground_guerrillas(game.op.where, AUC) gen_underground_guerrillas(game.op.where, CARTELS) - view.actions.next = 1 + if (did_maximum_damage(game.op.targeted)) + view.actions.next = 1 + else + view.actions.next = 0 }, piece(p) { push_undo() + game.op.targeted |= target_faction(p) set_active(p) if (--game.op.count === 0 || !has_any_underground_guerrilla(game.op.where)) game.state = "sweep" @@ -1615,7 +1659,7 @@ states.sweep_activate = operation({ push_undo() game.state = "sweep" }, -}) +} // OPERATION: ASSAULT @@ -1639,7 +1683,7 @@ function gen_exposed_piece(s, faction) { gen_piece_in_space(s, faction, BASE) } -states.assault = operation({ +states.assault = { prompt() { view.prompt = "Assault: Select space to eliminate enemy forces." @@ -1678,25 +1722,27 @@ states.assault = operation({ game.op.count >>= 1 }, end_operation, -}) +} -states.assault_space = operation({ +states.assault_space = { prompt() { view.prompt = `Assault: Remove ${game.op.count} enemy pieces in ${space_name[game.op.where]}.` view.where = game.op.where gen_any_govt_special() - // TODO: maximum damage among targeted factions - gen_exposed_piece(game.op.where, FARC) gen_exposed_piece(game.op.where, AUC) gen_exposed_piece(game.op.where, CARTELS) - view.actions.next = 1 + if (did_maximum_damage(game.op.targeted)) + view.actions.next = 1 + else + view.actions.next = 0 }, piece(p) { push_undo() + game.op.targeted |= target_faction(p) remove_piece(p) update_control() if (--game.op.count === 0 || !can_assault_space(game.op.where)) @@ -1706,7 +1752,7 @@ states.assault_space = operation({ push_undo() game.state = "assault" }, -}) +} // OPERATION: RALLY @@ -1716,7 +1762,7 @@ function rally_count() { return 1 } -states.rally = operation({ +states.rally = { prompt() { if (game.current === FARC) view.prompt = "Rally: Select City or Department without Support." @@ -1730,7 +1776,7 @@ states.rally = operation({ gen_special(AUC, "extort") gen_special(CARTELS, "cultivate") gen_special(CARTELS, "process") - gen_special(CARTELS, "bribe") + gen_special(CARTELS, "bribe", game.resources[CARTELS] < 3) } // Departments or Cities @@ -1770,9 +1816,9 @@ states.rally = operation({ game.op.count = rally_count() }, end_operation, -}) +} -states.rally_space = operation({ +states.rally_space = { prompt() { view.prompt = `Rally: Place up to ${game.op.count} Guerrillas.` view.where = game.op.where @@ -1816,9 +1862,9 @@ states.rally_space = operation({ push_undo() game.state = "rally" }, -}) +} -states.rally_base = operation({ +states.rally_base = { prompt() { if (game.op.count > 0) { view.prompt = `Rally: Replace ${game.op.count} Guerrillas with a Base.` @@ -1839,9 +1885,9 @@ states.rally_base = operation({ } update_control() }, -}) +} -states.rally_move = operation({ +states.rally_move = { prompt() { view.where = game.op.where @@ -1870,7 +1916,7 @@ states.rally_move = operation({ }) game.state = "rally" } -}) +} // OPERATION: MARCH @@ -1881,10 +1927,18 @@ function can_march_to(to) { return false } -states.march = operation({ +states.march = { prompt() { view.prompt = `March: Move Guerrillas to Cities/Departments/LoCs.` + if (game.sa) { + gen_special(FARC, "extort") + gen_special(AUC, "extort") + gen_special(CARTELS, "cultivate", count_pieces(AVAILABLE, CARTELS, BASE) === 15) + gen_special(CARTELS, "process") + gen_special(CARTELS, "bribe", game.resources[CARTELS] < 3) + } + if (can_select_op_space(1)) { for (let s = first_space; s <= last_dept; ++s) { if (is_selected_op_space(s)) @@ -1923,7 +1977,7 @@ states.march = operation({ game.op.march = [] }, end_operation, -}) +} function may_activate_marching_guerrillas() { if (is_loc(game.op.where)) @@ -1949,7 +2003,7 @@ function activate_marching_guerrillas(group) { } } -states.march_move = operation({ +states.march_move = { prompt() { view.prompt = `March: Move Guerrillas to ${space_name[game.op.where]}.` view.where = game.op.where @@ -1990,7 +2044,7 @@ states.march_move = operation({ game.op.march = 0 game.state = "march" }, -}) +} // OPERATION: ATTACK @@ -2031,10 +2085,16 @@ function has_exposed_enemy_piece(s, faction) { return false } -states.attack = operation({ +states.attack = { prompt() { view.prompt = "Attack: Select space with Guerrilla and enemy piece." + if (game.sa) { + gen_special(FARC, "extort") + gen_special(AUC, "extort") + gen_special(CARTELS, "bribe", game.resources[CARTELS] < 3) + } + if (can_select_op_space(1)) { for (let s = first_space; s <= last_space; ++s) { if (is_selected_op_space(s)) @@ -2060,9 +2120,9 @@ states.attack = operation({ game.op.where = s }, end_operation, -}) +} -states.attack_space = operation({ +states.attack_space = { prompt() { view.prompt = `Attack in ${space_name[game.op.where]}.` view.where = game.op.where @@ -2099,9 +2159,9 @@ states.attack_space = operation({ } }, ambush: goto_ambush, -}) +} -states.attack_place = operation({ +states.attack_place = { prompt() { view.prompt = `Attack in ${space_name[game.op.where]}: Place a Guerrilla.` view.where = game.op.where @@ -2110,31 +2170,35 @@ states.attack_place = operation({ piece(p) { push_undo() place_piece(p, game.op.where) + update_control() game.state = "attack_remove" game.op.count = 2 } -}) +} -states.attack_remove = operation({ +states.attack_remove = { prompt() { view.prompt = `Attack in ${space_name[game.op.where]}: Remove ${game.op.count} enemy pieces.` view.where = game.op.where - // TODO: maximum damage among targeted factions - - gen_remove_piece(game.op.where, GOVT) + gen_attack_piece(game.op.where, GOVT) if (game.current !== FARC) - gen_exposed_piece(game.op.where, FARC) + gen_attack_piece(game.op.where, FARC) if (game.current !== AUC) - gen_exposed_piece(game.op.where, AUC) + gen_attack_piece(game.op.where, AUC) if (game.current !== CARTELS) - gen_exposed_piece(game.op.where, CARTELS) + gen_attack_piece(game.op.where, CARTELS) - view.actions.next = 1 + if (did_maximum_damage(game.op.targeted)) + view.actions.next = 1 + else + view.actions.next = 0 }, piece(p) { push_undo() + game.op.targeted |= target_faction(p) remove_piece(p) + update_control() if (--game.op.count === 0 || !has_exposed_enemy_piece(game.op.where, game.current)) game.state = "attack" }, @@ -2142,11 +2206,11 @@ states.attack_remove = operation({ game.state = "attack" game.op.count = 0 } -}) +} // OPERATION: TERROR -states.terror = operation({ +states.terror = { prompt() { view.prompt = "Terror: Select space with Underground Guerrilla." @@ -2157,7 +2221,7 @@ states.terror = operation({ gen_special(FARC, "kidnap") gen_special(AUC, "extort") gen_special(AUC, "assassinate", game.op.spaces.length === 0) - gen_special(CARTELS, "bribe") + gen_special(CARTELS, "bribe", game.resources[CARTELS] < 3) } if (can_select_op_space(1)) { @@ -2188,7 +2252,7 @@ states.terror = operation({ do_terror(s) }, end_operation, -}) +} function do_terror(s) { let p = find_underground_guerrilla(s, game.current) @@ -2232,20 +2296,16 @@ function do_terror(s) { // === SPECIAL ACTIVITIES === -function operation(state) { - const special_activities = { - air_lift: goto_air_lift, - air_strike: goto_air_strike, - eradicate: goto_eradicate, - extort: goto_extort, - kidnap: goto_kidnap, - assassinate: goto_assassinate, - cultivate: goto_cultivate, - process: goto_process, - bribe: goto_bribe, - } - Object.assign(state, special_activities) - return state +const special_activities = { + air_lift: goto_air_lift, + air_strike: goto_air_strike, + eradicate: goto_eradicate, + extort: goto_extort, + kidnap: goto_kidnap, + assassinate: goto_assassinate, + cultivate: goto_cultivate, + process: goto_process, + bribe: goto_bribe, } function gen_special(faction, action, disable = false) { @@ -2353,7 +2413,7 @@ states.air_strike = { } }, piece(p) { - push_undo() + //push_undo() let s = game.pieces[p] log(`Air Strike in S${s}.`) remove_piece(p) @@ -2761,6 +2821,7 @@ states.assassinate_space = { }, piece(p) { remove_piece(p) + update_control() if (game.sa.spaces.length === 3) end_special_activity() @@ -2774,28 +2835,275 @@ states.assassinate_space = { function goto_cultivate() { push_undo() log_h3("Cultivate") - game.sa = { save: game.state } + game.sa = { + save: game.state, + where: -1, + } game.state = "cultivate" } +states.cultivate = { + prompt() { + view.prompt = "Cultivate: Select Department or City." + for (let s = first_pop; s <= last_pop; ++s) + if (count_bases(s) < 2) + gen_action_space(s) + }, + space(s) { + push_undo() + log(`Cultivate in S${s}.`) + game.sa.where = s + if (game.sa.save === "rally") + game.state = "cultivate_place" + else + game.state = "cultivate_move" + }, +} + +states.cultivate_place = { + prompt() { + view.prompt = `Cultivate: Place Base in ${space_name[game.sa.where]}.` + view.where = game.sa.where + gen_place_piece(game.sa.where, CARTELS, BASE) + }, + piece(p) { + push_undo() + place_piece(p, game.sa.where) + update_control() + end_special_activity() + }, +} + +states.cultivate_move = { + prompt() { + view.prompt = `Cultivate: Relocate Base to ${space_name[game.sa.where]}.` + view.where = game.sa.where + for_each_piece(CARTELS, BASE, p => { + if (game.pieces[p] !== game.sa.where && game.pieces[p] !== AVAILABLE) + gen_action_piece(p) + }) + }, + piece(p) { + push_undo() + move_piece(p, game.sa.where) + update_control() + end_special_activity() + }, +} + // SPECIAL ACTIVITY: PROCESS +function has_available_shipment() { + for (let i = 0; i < 4; ++i) + if (game.shipments[i] === AVAILABLE) + return true + return false +} + +function find_available_shipment() { + for (let i = 0; i < 4; ++i) + if (game.shipments[i] === AVAILABLE) + return i + return -1 +} + function goto_process() { push_undo() log_h3("Process") - game.sa = { save: game.state } + game.sa = { + save: game.state, + spaces: [], + where: -1, + } game.state = "process" } +states.process = { + prompt() { + view.prompt = "Process: Select spaces with Base." + for (let s = first_space; s <= last_space; ++s) + if (!set_has(game.sa.spaces, s) && has_piece(s, CARTELS, BASE)) + gen_action_space(s) + view.actions.end_activity = 1 + }, + space(s) { + push_undo() + log(`Process in S${s}.`) + set_add(game.sa.spaces, s) + game.sa.where = s + game.state = "process_space" + }, + end_activity() { + push_undo() + end_special_activity() + }, +} + +states.process_space = { + prompt() { + view.prompt = `Process: Remove Base or place Shipment with Guerrilla in ${space_name[game.sa.where]}.` + view.where = game.sa.where + gen_piece_in_space(game.sa.where, CARTELS, BASE) + if (has_available_shipment()) { + gen_piece_in_space(game.sa.where, CARTELS, GUERRILLA) + gen_piece_in_space(game.sa.where, FARC, GUERRILLA) + gen_piece_in_space(game.sa.where, AUC, GUERRILLA) + } + }, + piece(p) { + push_undo() + if (is_base(p)) { + game.resources[CARTELS] += 3 + remove_piece(p) + update_control() + } else { + let i = find_available_shipment() + game.shipments[i] = p + } + game.state = "process" + }, +} + // SPECIAL ACTIVITY: BRIBE function goto_bribe() { push_undo() log_h3("Bribe") - game.sa = { save: game.state } + game.sa = { + save: game.state, + spaces: [], + where: -1, + targeted: 0, + count: 0, + piece: -1, + } game.state = "bribe" } +function resume_bribe() { + if (game.sa.spaces.length === 3) + end_special_activity() + else + game.state = "bribe" +} + +states.bribe = { + prompt() { + view.prompt = `Bribe: Select up to ${3-game.sa.spaces.length} spaces.` + if (game.resources[CARTELS] >= 3) { + for (let s = first_space; s <= last_space; ++s) + if (!set_has(game.sa.spaces, s) && has_enemy_piece(s)) + gen_action_space(s) + } + view.actions.end_activity = 1 + }, + space(s) { + push_undo() + game.resources[CARTELS] -= 3 + set_add(game.sa.spaces, s) + game.sa.where = s + game.sa.targeted = 0 + game.state = "bribe_space" + }, + end_activity() { + push_undo() + end_special_activity() + }, +} + +states.bribe_space = { + prompt() { + view.prompt = "Bribe: Remove or flip 1 base, 2 cubes, or 2 guerrillas." + + // Nothing removed yet + if (game.sa.targeted === 0) { + gen_piece_in_space(game.sa.where, GOVT, TROOPS) + gen_piece_in_space(game.sa.where, GOVT, POLICE) + gen_piece_in_space(game.sa.where, GOVT, BASE) + gen_piece_in_space(game.sa.where, FARC, GUERRILLA) + gen_piece_in_space(game.sa.where, FARC, BASE) + gen_piece_in_space(game.sa.where, AUC, GUERRILLA) + gen_piece_in_space(game.sa.where, AUC, BASE) + if ( + has_piece(game.sa.where, FARC, GUERRILLA) || + has_piece(game.sa.where, AUC, GUERRILLA) || + has_piece(game.sa.where, CARTELS, GUERRILLA) + ) + view.actions.flip = 1 + else + view.actions.flip = 0 + } + + // Govt, targeted cube + else if (game.sa.targeted & (1 << GOVT)) { + gen_piece_in_space(game.sa.where, GOVT, TROOPS) + gen_piece_in_space(game.sa.where, GOVT, POLICE) + } + + // Not govt, targeted guerrilla + else { + gen_piece_in_space(game.sa.where, FARC, GUERRILLA) + gen_piece_in_space(game.sa.where, AUC, GUERRILLA) + } + + if (did_maximum_damage(game.sa.targeted)) + view.actions.next = 1 + else + view.actions.next = 0 + }, + piece(p) { + //push_undo() + remove_piece(p) + update_control() + if (game.sa.targeted || is_base(p)) + resume_bribe() + else + game.sa.targeted |= target_faction(p) + }, + flip() { + //push_undo() + game.state = "bribe_flip" + game.sa.count = 2 + game.sa.piece = -1 + }, + next() { + resume_bribe() + }, +} + +states.bribe_flip = { + prompt() { + view.prompt = `Bribe: Flip up to ${game.sa.count} Guerrillas.` + + gen_piece_in_space(game.sa.where, FARC, GUERRILLA) + gen_piece_in_space(game.sa.where, AUC, GUERRILLA) + gen_piece_in_space(game.sa.where, CARTELS, GUERRILLA) // TODO: can flip own? + + // Don't flip same piece twice... + if (game.sa.piece >= 0 && view.actions.piece) + set_delete(view.actions.piece, game.sa.piece) + + // No need to do maximum damage... + view.actions.next = 1 + }, + piece(p) { + if (is_underground(p)) + set_active(p) + else + set_underground(p) + game.sa.piece = p + let n = count_pieces(game.sa.where, FARC, GUERRILLA) + + count_pieces(game.sa.where, AUC, GUERRILLA) + + count_pieces(game.sa.where, CARTELS, GUERRILLA) // TODO: can flip own? + if (--game.sa.count === 0 || n === 1) + resume_bribe() + }, + next() { + //push_undo() + resume_bribe() + }, +} + // === GAME OVER === function goto_game_over(result, victory) { @@ -2984,6 +3292,8 @@ exports.action = function (state, role, action, arg) { pop_undo() else if (action === "remove") action_remove() + else if (game.op && action in special_activities) + special_activities[action](arg) else throw new Error("Invalid action: " + action) } diff --git a/tools/gendata.js b/tools/gendata.js index 4d773e9..37fbba9 100644 --- a/tools/gendata.js +++ b/tools/gendata.js @@ -174,8 +174,7 @@ function adjacent(an, bn) { // Cities data.first_space = 0 - -data.first_pop = 1 +data.first_pop = 0 data.first_city = spaces.length |