refactored (generalized) connected-search code. Added aux1 connected-group mining for stones and territory markers, and added flood-fill placement behavior for territory markers, with aux1 as an override to always place one.

pull/2/head
capitalthree 1 year ago
parent 6a77451fc0
commit 3e3a0ca98d
  1. 143
      rules.lua
  2. 9
      stones.lua

@ -25,14 +25,10 @@ local solid_drawtypes = {
glasslike_framed = true,
}
local S = ("S"):byte(1)
local W = ("W"):byte(1)
local E = ("E"):byte(1)
local C = ("C"):byte(1)
--[[
E: empty
E: air
E_: other empty
Etype: territory marker of some type
Stype: a go stone of some type
W: full block wall
WE0-WE3: edge concrete
@ -40,6 +36,11 @@ WC0-WC3: corner concrete
--]]
local function _check_position_uncached(pos)
local node = minetest.get_node(pos)
if node.name == "air" then
return "E"
end
local reg_item = minetest.registered_items[node.name]
if not reg_item then
@ -50,6 +51,10 @@ local function _check_position_uncached(pos)
return "S" .. reg_item.go_team
end
if reg_item.groups and reg_item.groups.go_territory_marker then
return "E" .. reg_item.go_team
end
if reg_item.pattern_def then
if reg_item.pattern_def.name == "edgy" then
return "WE" .. node.param2
@ -61,7 +66,7 @@ local function _check_position_uncached(pos)
if solid_drawtypes[reg_item.drawtype] and not reg_item.groups.falling_node then
return "W"
else
return "E"
return "E_"
end
end
@ -80,8 +85,8 @@ end
local function edge_check(pos, dir, terminate)
local under = check_position(pos + down)
if under:byte(1) == W and under:len() == 3 then
local corner = (under:byte(2) == C)
if under:sub(1, 1) == "W" and under:len() == 3 then
local corner = (under:sub(2, 2) == "C")
local edge_dir = tonumber(under:sub(3, 3))
if (dir == edge_dir) or (corner and (dir == (edge_dir+1)%4)) then
@ -106,7 +111,7 @@ local function neighbor_dirs(pos)
local neighbors = {}
for i,v in pairs(directions) do
if not edge_check(pos, i) then
if not (check_position(pos + directions[i]):byte(1) == W) then
if check_position(pos + directions[i]):sub(1, 1) ~= "W" then
neighbors[#neighbors+1] = i
end
end
@ -115,16 +120,11 @@ local function neighbor_dirs(pos)
return neighbors
end
--[[
Scans for a connected group, until enumerating the whole group, or
finding a liberty. Returns list of captured nodes, or empty list if
no captures.
--]]
local function check_captures(pos)
local function connected_search(pos, final_result, early_termination_filter)
local group = {pos}
local stones = {[minetest.hash_node_position(pos)] = pos}
local checked = {}
local team = check_position(pos):sub(2,-1)
local piece = check_position(pos)
local probe = 0
while probe < #group do
@ -138,10 +138,14 @@ local function check_captures(pos)
if (not stones[newhash]) and (not checked[newhash]) then
local newnode = check_position(newpos)
if newnode:byte(1) == E then
return {capture = false, stones = stones}
elseif newnode:byte(1) == S and
newnode:sub(2, -1) == team then
local filter_result
if early_termination_filter then
filter_result = early_termination_filter(newnode)
end
if filter_result then
filter_result.stones = stones
return filter_result
elseif newnode == piece then
stones[newhash] = newpos
group[#group+1] = newpos
else
@ -151,7 +155,61 @@ local function check_captures(pos)
end
end
return {capture = true, stones = stones}
final_result = final_result()
final_result.stones = stones
return final_result
end
local function _check_captures_filter(node)
if node:sub(1, 1) == "E" then
return {capture = false}
end
end
local function _check_captures_final()
return {capture = true}
end
local function check_captures(pos)
return connected_search(pos, _check_captures_final, _check_captures_filter)
end
local function _connected_group_final()
return {}
end
local function connected_group(pos)
return connected_search(pos, _connected_group_final)
end
local function territory_search(pos, max)
if check_position(pos) ~= "E" then
return {}
end
local team
return connected_search(pos,
function()
return {team = team}
end,
function(node)
if node == "E" then
max = max - 1
if max <= 0 then
return {}
end
elseif node ~= "E_" and ((node:sub(1, 1) == "E") or (node:sub(1, 1) == "S")) and node:len() > 1 then
node = node:sub(2, -1)
if team then
if team ~= node then
return {}
end
else
team = node
end
end
end
)
end
function lc_liberties.handle_placement(pos)
@ -160,7 +218,7 @@ function lc_liberties.handle_placement(pos)
--nodecore.node_sound(pos, "dug")
local our_stone = check_position(pos)
if not (our_stone:byte(1) == S) then
if our_stone:sub(1, 1) ~= "S" then
return
end
our_stone = our_stone:sub(2, -1)
@ -181,7 +239,7 @@ function lc_liberties.handle_placement(pos)
if not checked then
local node = check_position(new_pos)
if (node:byte(1) == S) and (not (node:sub(2, -1) == our_stone)) then
if (node:sub(1, 1) == "S") and (node:sub(2, -1) ~= our_stone) then
local captures = check_captures(new_pos)
captured = captured or captures.capture
captureses[#captureses+1] = captures
@ -226,4 +284,39 @@ function lc_liberties.handle_placement(pos)
end
end
function lc_liberties.handle_territory_fill(itemstack, placer, pointed_thing)
cache = {}
if placer.get_player_control and placer:get_player_control().aux1 then
return minetest.item_place(itemstack, placer, pointed_thing)
end
if pointed_thing and pointed_thing.above then
local territories = territory_search(pointed_thing.above, itemstack:get_count())
if territories.team then
local piece = modname .. ":territory_" .. territories.team
local count = 0
for i, v in pairs(territories.stones) do
nodecore.set_loud(v, {name = piece})
count = count + 1
end
itemstack:set_count(itemstack:get_count() - count)
end
end
return itemstack
end
function lc_liberties.handle_dig(pos, node, digger)
cache = {}
if digger.get_player_control and digger:get_player_control().aux1 then
for i, v in pairs(connected_group(pos).stones) do
minetest.node_dig(v, node, digger)
end
return true
else
return minetest.node_dig(pos, node, digger)
end
end

@ -68,7 +68,9 @@ local function reg(name, basename, basedef)
sounds = basedef.sounds,
on_construct = lc_liberties.handle_placement
on_construct = lc_liberties.handle_placement,
on_dig = lc_liberties.handle_dig,
})
local territory_name = modname .. ":territory_" .. string_lower(name)
@ -103,11 +105,16 @@ local function reg(name, basename, basedef)
snappy = 1,
falling_node = 1,
falling_repose = 1,
go_territory_marker = 1,
},
go_team = name,
sounds = basedef.sounds,
on_place = lc_liberties.handle_territory_fill,
on_dig = lc_liberties.handle_dig,
})
nodecore.register_craft({

Loading…
Cancel
Save