From 3e3a0ca98de9759c18fa8d5e6a807448f3048ff5 Mon Sep 17 00:00:00 2001 From: capitalthree Date: Tue, 21 Nov 2023 05:29:18 -0600 Subject: [PATCH] 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. --- rules.lua | 143 +++++++++++++++++++++++++++++++++++++++++++---------- stones.lua | 9 +++- 2 files changed, 126 insertions(+), 26 deletions(-) diff --git a/rules.lua b/rules.lua index 49f445f..a4d267e 100644 --- a/rules.lua +++ b/rules.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 \ No newline at end of file diff --git a/stones.lua b/stones.lua index de93274..c97c1d5 100644 --- a/stones.lua +++ b/stones.lua @@ -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({