// https://www.redblobgames.com/grids/hexagons/ const fs = require('fs') // Clear const CLEAR = 2; // Constricted const GAP = 1; // Rough (impassable) const OFFMAP = 0; const RIDGE = 0; const MARSH = 0; const MOUNTAIN = 0; // Road const TRAIL = 1; const TRACK = 2; const HIGHWAY = 4; const map_w = 25; const map_h = 9; let next = [ 1, map_w, map_w-1, -1, -map_w, -(map_w-1) ]; let nextdir = [ -1, 1, map_w, map_w-1, -map_w, -(map_w-1) ] let nextname = [ "E", "W", "NW", "NE", "SE", "SW" ]; let hex_exists = new Array(map_w*map_h).fill(1); let side_limit = new Array(map_w*map_h*3).fill(CLEAR); let side_road = new Array(map_w*map_h*3).fill(0); let hex_road = new Array(map_w*map_h).fill(0); function add_side(s, t) { if (side_limit[s]) side_limit[s] += "/" + t; else side_limit[s] = t; } function to_side_id(a, b) { if (a > b) { let c = b; b = a; a = c; } if (a + next[0] === b) return a * 3 + 0; else if (a + next[1] === b) return a * 3 + 1; else if (a + next[2] === b) return a * 3 + 2; throw new Error("not a hexside " + a + " to " + b); } function def_side(t,a,b) { side_limit[to_side_id(a,b)] = t; } function def_road(t, list) { for (let i = 0; i < list.length; ++i) if (t > hex_road[list[i]]) hex_road[list[i]] = t; for (let i = 1; i < list.length; ++i) { let s = to_side_id(list[i-1], list[i]); side_road[s] = t; if (side_limit[s] === MOUNTAIN) side_limit[s] = GAP; } } def_side(MARSH, 176, 152); def_side(MARSH, 179, 203); def_side(RIDGE, 144, 145); def_side(RIDGE, 120, 145); def_side(RIDGE, 121, 145); def_side(RIDGE, 121, 146); def_side(RIDGE, 122, 146); def_side(RIDGE, 144, 169); def_side(RIDGE, 144, 168); def_side(RIDGE, 143, 168); def_side(RIDGE, 167, 168); def_side(RIDGE, 167, 192); def_side(RIDGE, 191, 192); def_side(RIDGE, 191, 216); def_side(RIDGE, 215, 216); def_side(RIDGE, 217, 218); def_side(RIDGE, 217, 193); def_side(RIDGE, 192, 193); def_side(RIDGE, 168, 193); def_side(RIDGE, 168, 169); def_side(RIDGE, 69, 94); def_side(RIDGE, 67, 92); def_side(RIDGE, 68, 92); def_side(RIDGE, 68, 93); def_side(RIDGE, 65, 40); def_side(RIDGE, 65, 64); def_side(RIDGE, 65, 89); def_side(RIDGE, 65, 90); def_side(RIDGE, 66, 90); def_side(RIDGE, 66, 91); def_side(RIDGE, 39, 63); def_side(RIDGE, 37, 38); def_side(RIDGE, 36, 37); def_side(RIDGE, 36, 61); def_side(RIDGE, 36, 60); def_side(RIDGE, 109, 133); def_side(RIDGE, 108, 133); def_side(RIDGE, 204, 179); def_side(RIDGE, 204, 180); def_side(RIDGE, 205, 180); def_side(RIDGE, 31, 30); def_side(RIDGE, 31, 55); def_side(RIDGE, 55, 54); def_side(RIDGE, 78, 79); def_side(RIDGE, 30, 55); def_side(RIDGE, 54, 79); def_side(RIDGE, 103, 79); def_side(RIDGE, 176, 200); def_side(RIDGE, 176, 201); def_side(RIDGE, 177, 152); def_side(RIDGE, 177, 153); def_side(RIDGE, 178, 153); def_side(RIDGE, 153, 154); def_side(RIDGE, 129, 154); def_side(MOUNTAIN, 7, 8); def_side(MOUNTAIN, 7, 31); def_side(MOUNTAIN, 7, 32); def_side(MOUNTAIN, 8, 9); def_side(MOUNTAIN, 8, 32); def_side(MOUNTAIN, 8, 33); def_side(MOUNTAIN, 9, 33); def_side(MOUNTAIN, 9, 34); def_side(MOUNTAIN, 9, 10); def_side(MOUNTAIN, 32, 31); def_side(MOUNTAIN, 32, 33); def_side(MOUNTAIN, 32, 56); def_side(MOUNTAIN, 32, 57); def_side(MOUNTAIN, 33, 34); def_side(MOUNTAIN, 33, 57); def_side(MOUNTAIN, 33, 58); def_side(MOUNTAIN, 34, 10); def_side(MOUNTAIN, 34, 35); def_side(MOUNTAIN, 34, 58); def_side(MOUNTAIN, 34, 59); def_side(MOUNTAIN, 55, 80); def_side(MOUNTAIN, 56, 80); def_side(MOUNTAIN, 56, 55); def_side(MOUNTAIN, 56, 31); def_side(MOUNTAIN, 56, 81); def_side(MOUNTAIN, 56, 57); def_side(MOUNTAIN, 57, 58); def_side(GAP, 176, 152); def_side(GAP, 65, 40); def_side(GAP, 65, 64); def_side(GAP, 65, 89); def_side(GAP, 37, 38); def_side(GAP, 36, 37); def_side(GAP, 36, 61); def_side(GAP, 109, 133); def_side(GAP, 31, 30); def_side(GAP, 31, 55); def_side(GAP, 55, 54); def_side(GAP, 78, 79); def_side(GAP, 177, 153); def_side(GAP, 178, 153); def_side(GAP, 129, 154); def_road(HIGHWAY, [151,152,128,103,78,54,30,31,7,8,9,10,35,36,37,38,39,40,64,65,66,67,68,69,70,71,72,73,74]); def_road(HIGHWAY, [54,55,31]); def_road(HIGHWAY, [31,32,33,9]); def_road(HIGHWAY, [36,61,62,38]); def_road(TRACK, [78,79,80,81,82,58,59,35]); def_road(TRACK, [128,104,80]); def_road(TRACK, [83,59,60,61,86,87,88,89]); def_road(TRACK, [37,62,87]); def_road(TRACK, [64,89,113]); def_road(TRACK, [62,63,64]); def_road(TRACK, [213,189,165,141,117,93,69]); def_road(TRACK, [72,97,121]); def_road(TRAIL, [152,153,178,203,204,205,181,182,183,184,185,186,187]); def_road(TRAIL, [153,128,129,104,103]); def_road(TRAIL, [79,55]); def_road(TRAIL, [55,56,57,58,34,10]); def_road(TRAIL, [58,83,107,106,130,129]); def_road(TRAIL, [35,60,85,61]); def_road(TRAIL, [85,86]); def_road(TRAIL, [85,109,133,157,181]); def_road(TRAIL, [64,88]); def_road(TRAIL, [88,113,138,162,187]); def_road(TRAIL, [87,112,137,162]); def_road(TRAIL, [65,89,114,139,163,188,213]); def_road(TRAIL, [67,91,115,140,164,189]); function def_offmap(a, b) { for (let hex_id = 0; hex_id < map_w * map_h; ++hex_id) { if (hex_id >= a && hex_id <= b) { hex_exists[hex_id] = 0; side_limit[hex_id * 3 + 0] = OFFMAP; side_limit[hex_id * 3 + 1] = OFFMAP; side_limit[hex_id * 3 + 2] = OFFMAP; } for (let s = 0; s < 3; ++s) { let next_id = hex_id + next[s]; if (next_id >= a && next_id <= b) side_limit[hex_id * 3 + s] = OFFMAP; } } } def_offmap(0,6); def_offmap(11,29); def_offmap(41,53); def_offmap(75,77); // def_offmap(99,102); def_offmap(100,102); // 99 is exit hex def_offmap(124,127); // def_offmap(148,150); def_offmap(149,150); // 148 is exit hex def_offmap(173,175); def_offmap(197,199); // def_offmap(198,199); // 197 is exit hex def_offmap(222,224); // show spines // def_offmap(175,175, 1); // def_offmap(99,99, 1); // def_offmap(148,148, 1); // def_offmap(197,197, 1); // depression def_offmap(168,168); def_offmap(168,168); def_offmap(192,192); def_offmap(193,193); def_offmap(216,217); // Hexes not in 2004 edition: // def_offmap(200,202); // def_offmap(206,212); // def_offmap(214,221); // Terrain chart hexes // def_offmap(145,146); // def_offmap(168,172); // def_offmap(192,196); // def_offmap(216,221); // Qattara Depression and east of it // def_offmap(145,148); def_offmap(145,146); def_offmap(168,172); def_offmap(192,196); def_offmap(216,221); let regions = {}; function def_region(name, a, b) { if (!(name in regions)) regions[name] = []; for (let i = a; i <= b; ++i) regions[name].push(i); } def_region("Libya", 7, 10); def_region("Libya", 30, 40); def_region("Libya", 54, 64); def_region("Libya", 78, 88); def_region("Libya", 103, 113); def_region("Libya", 128, 138); def_region("Libya", 151, 162); def_region("Libya", 176, 187); def_region("Libya", 200, 211); def_region("Egypt", 65, 74); def_region("Egypt", 89, 98); def_region("Egypt", 114, 123); def_region("Egypt", 139, 144); def_region("Egypt", 163, 167); def_region("Egypt", 188, 191); def_region("Egypt", 212, 215); def_region("Sidi Omar", 89, 89); def_region("Tobruk", 37, 37); def_region("Sollum", 65, 65); def_region("El Agheila", 151, 151); def_region("Mersa Brega", 152, 152); def_region("East Line", 36, 40); def_region("East Line", 60, 74); def_region("East Line", 85, 98); def_region("East Line", 112, 123); def_region("East Line", 137, 144); def_region("East Line", 162, 167); def_region("East Line", 187, 191); def_region("West Line", 7, 10); def_region("West Line", 30, 35); def_region("West Line", 54, 59); def_region("West Line", 78, 83); def_region("West Line", 103, 107); def_region("West Line", 128, 130); def_region("Jebel el Akhdar", 7, 9); def_region("Jebel el Akhdar", 32, 34); def_region("Jebel el Akhdar", 56, 57); def_region("Sebkha el Segira", 152, 152); def_region("Sebkha el Segira", 176, 176); def_region("Sebket el Jeneinen", 179, 179); def_region("Sebket el Jeneinen", 203, 203); def_region("Qattara Depression", 144, 146); def_region("Qattara Depression", 168, 169); def_region("Qattara Depression", 192, 193); def_region("Qattara Depression", 216, 217); hex_name = { 7: "El Garib", 8: "Cyrene", 9: "El Gubba", 10: "Derna", 30: "Tocra", 31: "Barce", 32: "Marawa", 33: "Wadi Cuff", 35: "Gazala", 36: "Acroma", 37: "Tobruk", 38: "Belhamed", 39: "Gambut", 40: "Bardia", 54: "Benghazi", 55: "Er Regima", 56: "Charruba", 58: "Mechili", 59: "Rotonda Segnali", 60: "Sidi Mufta", 61: "Bir Harmat", 62: "El Adem", 63: "Sidi Rezegh", 64: "Ft. Capuzzo", 65: "Sollum", 66: "Buq Buq", 67: "Sidi Barrani", 69: "Mersa Matruh", 70: "Fuka", 71: "El Daba", 72: "El Alamein", 73: "El Hamam", 74: "Alexandria", 78: "Ghemines", 79: "Skeleidima", 80: "Msus", 83: "Tengeder", 85: "Bir Hacheim", 86: "Retma", 87: "Bir Gubi", 88: "Gabr Saleh", 89: "Sidi Omar", 90: "Bir Habata", 91: "Sofafi", 93: "Bir el Kenayis", 94: "Bir Khalda", 97: "Alam Halfa", 103: "Beda Fomm", 104: "Antelat", 106: "Ben Gania", 113: "Ft. Maddalena", 115: "Bir Khamsa", 116: "Bir el Qatrani", 121: "El Himeimat", 128: "Agedabia", 129: "Jebel el Matar", 130: "Haraga", 141: "Bir Fuad", 151: "El Agheila", 152: "Mersa Brega", 153: "El Haseiat", 176: "Maaten Giofer", 178: "Sahaba", 187: "Jarabub Oasis", 204: "Jalo Oasis", 213: "Siwa Oasis", // overrides 123: "El Himeimat E E", 147: "El Himeimat E SE", 99: "Map Edge", 148: "Map Edge", 197: "Map Edge", 4: "Malta", 47: "Exited", 48: "Return for Refit", 49: "Alexandria Queue", 102: "Return for Refit", 127: "El Agheila Queue", } function expand_names() { let hex_name2 = { } for (let hex_id = 0; hex_id < map_w * map_h; ++hex_id) { if (hex_exists[hex_id] && !hex_name[hex_id]) { for (let s = 0; s < 6; ++s) { let next_id = hex_id + nextdir[s]; if (hex_name[next_id]) { hex_name2[hex_id] = hex_name[next_id] + " " + nextname[s]; break; } } } } for (let hex_id in hex_name2) hex_name[hex_id] = hex_name2[hex_id]; } expand_names(); let units = []; const ARMOR = 0 const INFANTRY = 1 const ANTITANK = 2 const ARTILLERY = 3 const class_from_type = { "recon": ARMOR, "armor": ARMOR, "i-tank": ARMOR, "infantry": INFANTRY, "mech. inf.": INFANTRY, "mot. inf.": INFANTRY, "para": INFANTRY, "mob. a/t": ANTITANK, "mot. a/t": ANTITANK, "self prop. arty": ARTILLERY, "artillery": ARTILLERY, } const speed_from_type = { "recon": 4, "armor": 3, "i-tank": 3, // optional rule: allied i-tanks set this to 2 "mech. inf.": 3, "mob. a/t": 3, "self prop. arty": 3, "mot. inf.": 2, "mot. a/t": 2, "infantry": 1, "para": 1, "artillery": 1, } let unit_map = {}; function def_block(nationality, type, appearance, steps, elite, label, name) { let klass = class_from_type[type]; let speed = speed_from_type[type]; if (name in unit_map) throw new Error("duplicate block name:", name); unit_map[name] = 1 units.push({nationality, type, class: klass, speed, appearance, steps, elite, label, name}); } function sort_nation(u) { if (u.nationality === 'italian') return 0 if (u.nationality === 'german') return 1 return 2 } function sort_subnation(u) { function isn(x) { let [a, b] = u.name.split('/'); if (b) b = b.split('+'); return a.endsWith(x) || (b && b[0] === x) } switch (true) { case isn("IN"): return 3 case isn("SA"): return 4 case isn("AU"): return 5 case isn("NZ"): return 6 case isn("FF"): return 7 case isn("Pol"): return 8 } return 2 } function sort_name(u) { if (u.nationality === 'italian') return 0 if (u.nationality === 'german') return 1 return sort_subnation(u) } function cmp_block(a, b) { let x = sort_name(a) * 10000 + a.class * 1000 + (1-a.elite) * 10 + (4-a.speed) * 100 + (4-a.steps) let y = sort_name(b) * 10000 + b.class * 1000 + (1-b.elite) * 10 + (4-b.speed) * 100 + (4-b.steps) // let x = sort_nation(a) * 10000 + a.class * 1000 + (1-a.elite) * 10 + (4-a.speed) * 100 + sort_subnation(a) // let y = sort_nation(b) * 10000 + b.class * 1000 + (1-b.elite) * 10 + (4-b.speed) * 100 + sort_subnation(b) if (x < y) return -1 if (x > y) return 1 if (parseInt(a.name) < parseInt(b.name)) return -1 if (parseInt(a.name) > parseInt(b.name)) return 1 if (a.name < b.name) return -1 if (a.name > b.name) return 1 return 0 } let S = "S"; let M = "M"; let T = "T"; //def_block("italian", battle block 1); def_block("italian", "infantry", S, 4, 0, 12, "Tre"); def_block("italian", "infantry", S, 4, 0, 13, "Pav"); def_block("italian", "infantry", S, 4, 0, 14, "Bre"); def_block("italian", "infantry", S, 3, 0, 15, "Bol"); def_block("italian", "infantry", S, 3, 0, 16, "Sav"); def_block("italian", "mech. inf.", 7, 4, 0, 21, "Tri"); def_block("italian", "infantry", 7, 2, 0, 22, "Sab"); def_block("italian", "armor", 11, 3, 0, 23, "Lit"); def_block("italian", "mot. inf.", 17, 3, 0, 24, "Fas"); def_block("italian", "mot. inf.", 19, 3, 0, 25, "Cen"); def_block("italian", "infantry", 19, 2, 0, 26, "Pis"); def_block("italian", "para", M, 4, 0, 27, "Fol"); def_block("italian", "artillery", S, 2, 0, 28, "Ita"); def_block("italian", "armor", S, 4, 0, 29, "Ari"); //def_block("german", battle block 1); //def_block("german", battle block 2); //def_block("german", battle block 3); def_block("german", "armor", S, 3, 1, 14, "21/5"); def_block("german", "recon", S, 1, 1, 15, "21/3"); def_block("german", "mech. inf.", S, 3, 1, 16, "21/104"); def_block("german", "recon", 3, 1, 1, 17, "15/33"); def_block("german", "mob. a/t", 3, 2, 1, 18, "88mm/A"); def_block("german", "mech. inf.", 3, 3, 1, 19, "15/115"); def_block("german", "armor", 5, 3, 1, 21, "15/8"); def_block("german", "recon", 5, 1, 1, 22, "90/580"); def_block("german", "mot. inf.", 5, 2, 1, 23, "90/361"); def_block("german", "mot. a/t", 5, 2, 1, 24, "50mm"); def_block("german", "artillery", 7, 2, 1, 25, "/104"); def_block("german", "mot. inf.", 7, 2, 1, 26, "90/200"); def_block("german", "mob. a/t", 11, 2, 1, 27, "88mm/B"); def_block("german", "mech. inf.", 11, 2, 1, 28, "90/sv288"); def_block("german", "mech. inf.", 11, 2, 1, 29, "90/346"); def_block("german", "para", M, 3, 1, 31, "Ram"); def_block("german", "infantry", M, 3, 1, 32, "164/382+433"); def_block("german", "infantry", M, 2, 1, 33, "164/125"); def_block("german", "mech. inf.", 3, 3, 1, 34, "90/155"); def_block("german", "mot. a/t", 17, 2, 1, 35, "76mm"); // def_block("allied", bb 1 // def_block("allied", bb 2 // def_block("allied", bb 3 def_block("allied", "armor", S, 2, 0, 14, "2/3"); def_block("allied", "mech. inf.", S, 2, 0, 15, "2/SG"); def_block("allied", "mech. inf.", S, 2, 0, 16, "4IN/3m"); def_block("allied", "mot. inf.", S, 4, 0, 17, "9AU/20"); def_block("allied", "mot. inf.", S, 4, 0, 18, "70/14+16"); def_block("allied", "mot. inf.", S, 3, 0, 19, "70/23"); def_block("allied", "armor", T, 1, 1, 21, "Matilda/A"); def_block("allied", "mech. inf.", T, 3, 0, 22, "7/SG"); def_block("allied", "mech. inf.", T, 3, 0, 23, "7/22G"); def_block("allied", "mot. inf.", T, 3, 0, 24, "/Pol"); def_block("allied", "mot. inf.", T, 3, 0, 25, "7AU/18"); def_block("allied", "artillery", T, 2, 0, 26, "/Tob"); def_block("allied", "mot. inf.", 18, 4, 0, 27, "51H/152"); def_block("allied", "self prop. arty", 18, 2, 0, 28, "Priest"); def_block("allied", "artillery", 18, 4, 0, 29, "/C"); def_block("allied", "armor", 2, 4, 0, 31, "7/7"); def_block("allied", "mech. inf.", 2, 3, 0, 32, "4IN/7m"); def_block("allied", "mot. inf.", 2, 3, 0, 33, "4IN/5"); def_block("allied", "mot. inf.", 2, 3, 0, 34, "4IN/11"); def_block("allied", "armor", 4, 1, 1, 35, "Matilda/B"); def_block("allied", "i-tank", 4, 4, 0, 36, "/1AT"); def_block("allied", "armor", 4, 3, 0, 37, "7/4"); def_block("allied", "recon", 4, 2, 0, 38, "7"); def_block("allied", "artillery", 20, 3, 0, 39, "/D"); def_block("allied", "mot. a/t", 6, 3, 0, 41, "2#"); def_block("allied", "mech. inf.", 6, 4, 0, 42, "2NZ/4"); def_block("allied", "mech. inf.", 6, 4, 0, 43, "2NZ/5"); def_block("allied", "mech. inf.", 6, 4, 0, 44, "2NZ/6"); def_block("allied", "mot. inf.", 6, 4, 0, 45, "1SA/2+5"); def_block("allied", "mot. inf.", 6, 3, 0, 46, "1SA/1"); def_block("allied", "recon", 8, 2, 0, 47, "1SA"); def_block("allied", "mot. inf.", 8, 3, 0, 48, "1SA/3"); def_block("allied", "mot. inf.", 8, 3, 0, 49, "2SA/4+6"); def_block("allied", "armor", 8, 4, 0, 51, "1/22"); def_block("allied", "i-tank", 8, 3, 0, 52, "/32AT"); def_block("allied", "artillery", 8, 2, 0, 53, "/A"); def_block("allied", "armor", 10, 4, 0, 54, "1/2"); def_block("allied", "mech. inf.", 10, 3, 0, 55, "1/201G"); def_block("allied", "mech. inf.", 10, 2, 0, 56, "1/SG"); def_block("allied", "mot. inf.", 10, 3, 0, 57, "5IN/29"); def_block("allied", "armor", 12, 2, 1, 58, "Grant"); def_block("allied", "mot. a/t", 12, 3, 0, 59, "6#/A"); def_block("allied", "mech. inf.", 14, 2, 0, 61, "10IN/161m"); def_block("allied", "mot. inf.", 14, 4, 0, 62, "5IN/9+10"); def_block("allied", "mot. inf.", 14, 4, 0, 63, "10IN/21+25"); def_block("allied", "mot. inf.", 14, 2, 0, 64, "8IN/18"); def_block("allied", "mot. inf.", 14, 4, 0, 65, "FF/2"); def_block("allied", "artillery", 14, 4, 0, 66, "/B"); def_block("allied", "armor", 20, 3, 1, 67, "Sher/B"); def_block("allied", "armor", 20, 3, 0, 68, "8/9"); def_block("allied", "armor", 20, 3, 0, 69, "8/24"); def_block("allied", "armor", 16, 4, 0, 71, "10/8"); def_block("allied", "armor", 16, 3, 0, 72, "10/23"); def_block("allied", "mot. inf.", 16, 4, 0, 73, "9AU/26"); def_block("allied", "mot. inf.", 16, 4, 0, 74, "9AU/24"); def_block("allied", "mot. inf.", 16, 4, 0, 75, "44/131+133"); def_block("allied", "mot. inf.", 16, 2, 0, 76, "44/132"); def_block("allied", "armor", 18, 3, 1, 77, "Sher/A"); def_block("allied", "mot. a/t", 18, 4, 0, 78, "6#/B"); def_block("allied", "mot. inf.", 18, 4, 0, 79, "51H/154"); units.sort(cmp_block) let all_hexes = hex_exists.map((x,i) => x ? i : 0).filter(x => x > 0) function gen_array(name, fn) { data += "const " + name + " = [" + units.map(fn).join(",") + "]\n" } let data = "// DON'T EDIT - AUTOGENERATED\n" data += "const all_hexes = " + JSON.stringify(all_hexes) + "\n" data += "const hex_exists = " + JSON.stringify(hex_exists) + "\n" data += "const hex_road = " + JSON.stringify(hex_road) + "\n" data += "const side_road = " + JSON.stringify(side_road) + "\n" data += "const side_limit = " + JSON.stringify(side_limit) + "\n" data += "const hex_name = " + JSON.stringify(hex_name) + "\n" data += "const regions = " + JSON.stringify(regions) + "\n" gen_array("unit_name", u => JSON.stringify(u.name)) gen_array("unit_appearance", u => JSON.stringify(u.appearance)) gen_array("unit_max_steps", u => u.steps) gen_array("unit_elite", u => u.elite) gen_array("unit_class", u => u.class) gen_array("unit_speed", u => u.speed) data += "if (typeof module !== 'undefined')\n\tmodule.exports = {\n" data += "\t\tall_hexes, hex_exists, hex_name, hex_road, side_road, side_limit, regions,\n" data += "\t\tunit_name, unit_appearance, unit_max_steps, unit_elite, unit_class, unit_speed,\n" data += "\t}\n" fs.writeFileSync("data.js", data) fs.writeFileSync("rawdata.js", "exports.units = " + JSON.stringify(units)) // Show ranges let test = "" function gen_test_range(fn) { let ranges = []; let start = -1; for (let p = 0; p < units.length; ++p) { if (fn(units[p])) { if (start < 0) start = p; } else { if (start >= 0) { ranges.push([start,p-1]) start = -1; } } } if (start >= 0) ranges.push([start,units.length-1]) return ranges.map(([a,b])=>`(u >= ${a} && u <= ${b})`).join(" || ") } function gen_test(name, fn) { test += "function " + name + "(u) { return " + gen_test_range(fn) + " }\n" } gen_test("is_armor_unit", u => u.class === ARMOR) gen_test("is_artillery_unit", u => u.class === ARTILLERY) gen_test("is_infantry_unit", u => u.class === INFANTRY) gen_test("is_antitank_unit", u => u.class === ANTITANK) gen_test("is_recon_unit", u => u.speed === 4) gen_test("is_mechanized_unit", u => u.speed === 3) gen_test("is_motorized_unit", u => u.speed === 2) gen_test("is_leg_unit", u => u.speed === 1) gen_test("is_elite_unit", u => u.elite) gen_test("is_italian_unit", u => u.nationality === 'italian') gen_test("is_german_unit", u => u.nationality === 'german') gen_test("is_axis_unit", u => u.nationality !== 'allied') gen_test("is_allied_unit", u => u.nationality === 'allied') console.log(test)