summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2024-05-23 14:11:43 +0200
committerTor Andersson <tor@ccxvii.net>2024-05-30 21:59:25 +0200
commit30f288a20651b950b25ce8f9289a122bc6d4785d (patch)
tree1e1f84f531d4f8e1dbede277799241fa15b0f3e9 /rules.js
parentc397fedb0d4d801472584c1495e539058617c5ba (diff)
downloadfriedrich-30f288a20651b950b25ce8f9289a122bc6d4785d.tar.gz
WIP search move
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js390
1 files changed, 331 insertions, 59 deletions
diff --git a/rules.js b/rules.js
index 7aa3f8e..399572a 100644
--- a/rules.js
+++ b/rules.js
@@ -1043,15 +1043,14 @@ states.movement = {
game.count = 0
game.major = 1
if (is_supply_train(p))
- game.state = "move_supply_train"
+ resume_move_supply_train()
else
- game.state = "move_general"
+ resume_move_general()
},
end_movement() {
push_undo()
goto_recruit()
},
-
}
function has_any_piece(to) {
@@ -1068,6 +1067,13 @@ function has_friendly_supply_train(to) {
return false
}
+function has_enemy_supply_train(to) {
+ for (let p of all_enemy_trains[game.power])
+ if (game.pos[p] === to)
+ return true
+ return false
+}
+
function has_enemy_piece(to) {
for (let p of all_enemy_generals[game.power])
if (game.pos[p] === to)
@@ -1087,6 +1093,13 @@ function has_any_other_general(to) {
return false
}
+function has_own_general(to) {
+ for (let p of all_power_generals[game.power])
+ if (game.pos[p] === to)
+ return true
+ return false
+}
+
function count_pieces(to) {
let n = 0
for (let s of game.pos)
@@ -1095,6 +1108,14 @@ function count_pieces(to) {
return n
}
+function can_move_train_to(to) {
+ return !has_any_piece(to)
+}
+
+function can_continue_train_from(from) {
+ return true
+}
+
function can_move_general_to(to) {
if (has_friendly_supply_train(to))
return false
@@ -1105,7 +1126,136 @@ function can_move_general_to(to) {
return true
}
-states.move_supply_train = {
+function can_continue_general_from(from) {
+ if (has_enemy_supply_train(from))
+ return false
+ if (has_own_general(from))
+ return false
+ return true
+}
+
+function search_move(from, range, road_type, can_move_to, can_continue_from) {
+ let seen = [ from, -1 ]
+ let queue = [ from << 4 ]
+ while (queue.length > 0) {
+ let item = queue.shift()
+ let here = item >> 4
+ let dist = (item & 15) + 1
+ for (let next of data.cities[road_type][here]) {
+ if (map_has(seen, next))
+ continue
+ if (!can_move_to(next))
+ continue
+ if (dist <= range) {
+ map_set(seen, next, here)
+ if (can_continue_from(next))
+ queue.push((next << 4) | dist)
+ }
+ }
+ }
+ return seen
+}
+
+function resume_move_supply_train() {
+ if (game.count === 2 + game.major) {
+ game.state = "movement"
+ delete game.move_major
+ delete game.move_minor
+ } else {
+ let here = game.pos[game.selected[0]]
+ game.state = "move_supply_train"
+ if (game.major && game.count < 3)
+ game.move_major = search_move(here, 3 - game.count, "major_roads", can_move_train_to, can_continue_train_from)
+ else
+ game.move_major = []
+ if (game.count < 2)
+ game.move_minor = search_move(here, 2 - game.count, "adjacent", can_move_train_to, can_continue_train_from)
+ else
+ game.move_minor = []
+ }
+}
+
+function resume_move_general() {
+ if (game.count === 3 + game.major) {
+ game.state = "movement"
+ delete game.move_major
+ delete game.move_minor
+ } else {
+ let here = game.pos[game.selected[0]]
+ game.state = "move_general"
+ if (game.major && game.count < 4)
+ game.move_major = search_move(here, 4 - game.count, "major_roads", can_move_general_to, can_continue_general_from)
+ else
+ game.move_major = []
+ if (game.count < 3)
+ game.move_minor = search_move(here, 3 - game.count, "adjacent", can_move_general_to, can_continue_general_from)
+ else
+ game.move_minor = []
+ }
+}
+
+function move_general_to(to) {
+ let pow = game.power
+ let who = game.selected[0]
+ let from = game.pos[who]
+ let stop = false
+
+ for (let p of game.selected) {
+ set_add(game.moved, p)
+ game.pos[p] = to
+ }
+
+ // uniting stacks (turn all oos if one is oos)
+ let oos = false
+ for (let p of all_power_generals[game.power])
+ if (game.pos[p] === to && is_out_of_supply(p))
+ oos = true
+ if (oos)
+ for (let p of all_power_generals[game.power])
+ if (game.pos[p] === to)
+ set_out_of_supply(p)
+
+ // conquer space
+ if (is_conquest_space(pow, from) && !set_has(game.conquest, from)) {
+ if (is_protected_from_conquest(from)) {
+ set_add(game.retro, from)
+ } else {
+ log("Conquered S" + from)
+ set_add(game.conquest, from)
+ }
+ }
+
+ // re-conquer space
+ if (is_reconquest_space(pow, from) && set_has(game.conquest, from)) {
+ if (is_protected_from_reconquest(from)) {
+ set_add(game.retro, from)
+ } else {
+ log("Reconquered S" + from)
+ set_delete(game.conquest, from)
+ }
+ }
+
+ // eliminate supply train
+ for (let p of all_enemy_trains[pow]) {
+ if (game.pos[p] === to) {
+ log("Eliminate P" + p)
+ game.pos[p] = ELIMINATED
+ stop = true
+ }
+ }
+
+ // uniting stacks: flag all as moved and stop moving
+ for (let p of all_power_generals[pow]) {
+ if (game.pos[p] === to && !set_has(game.selected, p)) {
+ set_add(game.moved, p)
+ stop = true
+ }
+ }
+
+ return stop
+}
+
+states.move_supply_train_NEW = {
prompt() {
prompt("Move supply train.")
view.selected = game.selected
@@ -1113,6 +1263,12 @@ states.move_supply_train = {
let who = game.selected[0]
let here = game.pos[who]
+ if (game.move_major)
+ map_for_each_key(game.move_major, s => { if (s !== here) gen_action_space(s) })
+ if (game.move_minor)
+ map_for_each_key(game.move_minor, s => { if (s !== here) gen_action_space(s) })
+
+ /*
if (game.count < 2 + game.major)
for (let next of data.cities.major_roads[here])
if (!has_any_piece(next))
@@ -1121,6 +1277,7 @@ states.move_supply_train = {
for (let next of data.cities.roads[here])
if (!has_any_piece(next))
gen_action_space(next)
+ */
if (game.count > 0) {
gen_action_piece(who)
@@ -1132,6 +1289,72 @@ states.move_supply_train = {
},
stop() {
game.state = "movement"
+ delete game.move_major
+ delete game.move_minor
+ },
+ space(to) {
+ let who = game.selected[0]
+ let from = game.pos[who]
+
+ // TODO: path?
+
+ set_add(game.moved, who)
+ game.pos[who] = to
+
+ log("P" + who + " to S" + to)
+
+ let m = map_get(game.move_major, to, 0)
+ if (m > 0) {
+ while (m > 0) {
+ // TODO: reverse
+ log(">S" + m)
+ m = map_get(game.move_major, m)
+ ++ game.count
+ }
+ } else {
+ m = map_get(game.move_minor, to, 0)
+ while (m > 0) {
+ // TODO: reverse
+ log(">S" + m)
+ m = map_get(game.move_minor, m)
+ ++ game.count
+ }
+ game.major = 0
+ }
+
+ resume_move_supply_train()
+ },
+}
+
+states.move_supply_train_OLD = {
+ prompt() {
+ prompt("Move supply train.")
+ view.selected = game.selected
+
+ let who = game.selected[0]
+ let here = game.pos[who]
+
+ if (game.count < 2 + game.major)
+ for (let next of data.cities.major_roads[here])
+ if (!has_any_piece(next))
+ gen_action_space(next)
+ if (game.count < 2)
+ for (let next of data.cities.roads[here])
+ if (!has_any_piece(next))
+ gen_action_space(next)
+
+ if (game.count > 0) {
+ gen_action_piece(who)
+ view.actions.stop = 1
+ }
+ },
+ piece(_) {
+ this.stop()
+ },
+ stop() {
+ game.state = "movement"
+ delete game.move_major
+ delete game.move_minor
},
space(to) {
let who = game.selected[0]
@@ -1141,6 +1364,7 @@ states.move_supply_train = {
if (!set_has(data.cities.major_roads[here], to))
game.major = 0
+
set_add(game.moved, who)
game.pos[who] = to
@@ -1149,7 +1373,96 @@ states.move_supply_train = {
},
}
-states.move_general = {
+states.move_general_NEW = {
+ prompt() {
+ prompt("Move general.")
+ view.selected = game.selected
+
+ let who = game.selected[0]
+ let here = game.pos[who]
+
+ if (game.count === 0) {
+ if (game.selected.length > 1)
+ view.actions.detach = 1
+ else
+ view.actions.detach = 0
+
+ let s_take = count_stacked_take()
+ let s_give = count_stacked_give()
+ let u_take = count_unstacked_take()
+ let u_give = count_unstacked_give()
+
+ if (s_take > 0 && u_give > 0)
+ view.actions.take = 1
+ if (s_give > 0 && u_take > 0)
+ view.actions.give = 1
+ } else {
+ gen_action_piece(who)
+ view.actions.stop = 1
+ }
+
+ if (game.move_major)
+ map_for_each_key(game.move_major, s => { if (s !== here) gen_action_space_or_piece(s) })
+ if (game.move_minor)
+ map_for_each_key(game.move_minor, s => { if (s !== here) gen_action_space_or_piece(s) })
+ },
+ take() {
+ game.state = "move_take"
+ },
+ give() {
+ game.state = "move_give"
+ },
+ detach() {
+ game.state = "move_detach"
+ },
+ piece(p) {
+ if (p === game.selected[0])
+ this.stop()
+ else
+ this.space(game.pos[p])
+ },
+ stop() {
+ for (let p of game.selected)
+ set_add(game.moved, p)
+ game.state = "movement"
+ delete game.move_major
+ delete game.move_minor
+ },
+ space(to) {
+ let who = game.selected[0]
+ let path = [ to ]
+
+ let m = map_get(game.move_major, to, -1)
+ if (m >= 0) {
+ while (m >= 0) {
+ path.unshift(m)
+ m = map_get(game.move_major, m, -1)
+ ++game.count
+ }
+ } else {
+ m = map_get(game.move_minor, to, -1)
+ while (m >= 0) {
+ path.unshift(m)
+ m = map_get(game.move_minor, m, -1)
+ ++game.count
+ }
+ game.major = 0
+ }
+
+ log("P" + who + " " + path.map(s => "S" + s).join(" > "))
+
+ let stop = false
+ for (let s of path)
+ stop ||= move_general_to(s)
+
+ if (stop)
+ this.stop()
+ else
+ resume_move_general()
+ },
+}
+
+states.move_general_OLD = {
prompt() {
prompt("Move general.")
view.selected = game.selected
@@ -1206,9 +1519,10 @@ states.move_general = {
for (let p of game.selected)
set_add(game.moved, p)
game.state = "movement"
+ delete game.move_major
+ delete game.move_minor
},
space(to) {
- let pow = game.power
let who = game.selected[0]
let from = game.pos[who]
@@ -1217,63 +1531,16 @@ states.move_general = {
if (!set_has(data.cities.major_roads[from], to))
game.major = 0
- // uniting stacks (flag all as moved)
- for (let p of game.selected) {
- set_add(game.moved, p)
- game.pos[p] = to
- }
-
- // uniting stacks (turn all oos if one is oos)
- let oos = false
- for (let p of all_power_generals[game.power])
- if (game.pos[p] === to && is_out_of_supply(p))
- oos = true
- if (oos)
- for (let p of all_power_generals[game.power])
- if (game.pos[p] === to)
- set_out_of_supply(p)
-
- if (is_conquest_space(pow, from) && !set_has(game.conquest, from)) {
- if (is_protected_from_conquest(from)) {
- set_add(game.retro, from)
- } else {
- log("Conquered S" + from)
- set_add(game.conquest, from)
- }
- }
-
- if (is_reconquest_space(pow, from) && set_has(game.conquest, from)) {
- if (is_protected_from_reconquest(from)) {
- set_add(game.retro, from)
- } else {
- log("Reconquered S" + from)
- set_delete(game.conquest, from)
- }
- }
-
- for (let p of all_enemy_trains[pow]) {
- if (game.pos[p] === to) {
- log("Eliminate P" + p)
- game.pos[p] = ELIMINATED
- game.state = "movement"
- return
- }
- }
-
- for (let p of all_power_generals[pow]) {
- if (game.pos[p] === to && !set_has(game.selected, p)) {
- set_add(game.moved, p)
- game.state = "movement"
- return
- }
- }
-
- if (++game.count === 3 + game.major) {
+ if (move_general_to(to) || ++game.count === 3 + game.major)
game.state = "movement"
- }
},
}
+states.move_general = states.move_general_OLD
+states.move_supply_train = states.move_supply_train_OLD
+//states.move_general = states.move_general_NEW
+//states.move_supply_train = states.move_supply_train_NEW
+
states.move_detach = {
prompt() {
prompt("Detach general.")
@@ -2864,3 +3131,8 @@ function map_set(map, key, value) {
}
array_insert_pair(map, a<<1, key, value)
}
+
+function map_for_each_key(map, f) {
+ for (let i = 0; i < map.length; i += 2)
+ f(map[i])
+}