Compare commits

..

8 Commits

  1. 23
      LICENSE
  2. 181
      rules.lua
  3. 81
      stones.lua

@ -0,0 +1,23 @@
Copyright (C)2023 capitalthree
Portions Copyright (C)2018-2023 Aaron Suen <warr1024@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject
to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -25,21 +25,23 @@ local solid_drawtypes = {
glasslike_framed = true, 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 Stype: a go stone of some type
W: full block wall W: full block wall
WE0-WE3: edge concrete WE0-WE3: edge concrete
WC0-WC3: corner concrete WC0-WC3: corner concrete
WB: other goban concrete
--]] --]]
local function _check_position_uncached(pos) local function _check_position_uncached(pos)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if node.name == "air" then
return "E"
end
local reg_item = minetest.registered_items[node.name] local reg_item = minetest.registered_items[node.name]
if not reg_item then if not reg_item then
@ -50,18 +52,26 @@ local function _check_position_uncached(pos)
return "S" .. reg_item.go_team return "S" .. reg_item.go_team
end 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 then
if reg_item.pattern_def.name == "edgy" then if reg_item.pattern_def.name == "edgy" then
return "WE" .. node.param2 return "WE" .. node.param2
elseif reg_item.pattern_def.name == "corny" then elseif reg_item.pattern_def.name == "corny" then
return "WC" .. node.param2 return "WC" .. node.param2
elseif reg_item.pattern_def.name == "crossy" then
return "WB"
elseif reg_item.pattern_def.name == "starcrossy" then
return "WB"
end end
end end
if solid_drawtypes[reg_item.drawtype] and not reg_item.groups.falling_node then if solid_drawtypes[reg_item.drawtype] and not reg_item.groups.falling_node then
return "W" return "W"
else else
return "E" return "E_"
end end
end end
@ -80,8 +90,8 @@ end
local function edge_check(pos, dir, terminate) local function edge_check(pos, dir, terminate)
local under = check_position(pos + down) local under = check_position(pos + down)
if under:byte(1) == W and under:len() == 3 then if under:sub(1, 1) == "W" and under:len() == 3 then
local corner = (under:byte(2) == C) local corner = (under:sub(2, 2) == "C")
local edge_dir = tonumber(under:sub(3, 3)) local edge_dir = tonumber(under:sub(3, 3))
if (dir == edge_dir) or (corner and (dir == (edge_dir+1)%4)) then if (dir == edge_dir) or (corner and (dir == (edge_dir+1)%4)) then
@ -106,7 +116,7 @@ local function neighbor_dirs(pos)
local neighbors = {} local neighbors = {}
for i,v in pairs(directions) do for i,v in pairs(directions) do
if not edge_check(pos, i) then 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 neighbors[#neighbors+1] = i
end end
end end
@ -115,16 +125,11 @@ local function neighbor_dirs(pos)
return neighbors return neighbors
end end
--[[ local function connected_search(pos, final_result, early_termination_filter)
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 group = {pos} local group = {pos}
local stones = {[minetest.hash_node_position(pos)] = pos} local stones = {[minetest.hash_node_position(pos)] = pos}
local checked = {} local checked = {}
local team = check_position(pos):sub(2,-1) local piece = check_position(pos)
local probe = 0 local probe = 0
while probe < #group do while probe < #group do
@ -138,10 +143,14 @@ local function check_captures(pos)
if (not stones[newhash]) and (not checked[newhash]) then if (not stones[newhash]) and (not checked[newhash]) then
local newnode = check_position(newpos) local newnode = check_position(newpos)
if newnode:byte(1) == E then local filter_result
return {capture = false, stones = stones} if early_termination_filter then
elseif newnode:byte(1) == S and filter_result = early_termination_filter(newnode)
newnode:sub(2, -1) == team then end
if filter_result then
filter_result.stones = stones
return filter_result
elseif newnode == piece then
stones[newhash] = newpos stones[newhash] = newpos
group[#group+1] = newpos group[#group+1] = newpos
else else
@ -151,16 +160,70 @@ local function check_captures(pos)
end end
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 end
function lc_liberties.handle_placement(pos) function lc_liberties.handle_placement(pos)
cache = {} cache = {}
minetest.chat_send_all(tostring(pos)) --minetest.chat_send_all(tostring(pos))
--nodecore.node_sound(pos, "dug") --nodecore.node_sound(pos, "dug")
local our_stone = check_position(pos) local our_stone = check_position(pos)
if not (our_stone:byte(1) == S) then if our_stone:sub(1, 1) ~= "S" then
return return
end end
our_stone = our_stone:sub(2, -1) our_stone = our_stone:sub(2, -1)
@ -181,7 +244,7 @@ function lc_liberties.handle_placement(pos)
if not checked then if not checked then
local node = check_position(new_pos) 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) local captures = check_captures(new_pos)
captured = captured or captures.capture captured = captured or captures.capture
captureses[#captureses+1] = captures captureses[#captureses+1] = captures
@ -192,22 +255,78 @@ function lc_liberties.handle_placement(pos)
if captured then if captured then
for i,v in pairs(captureses) do for i,v in pairs(captureses) do
if v.capture then if v.capture then
local proximal = {} local stone = minetest.get_node(v.stones[next(v.stones)]).name
local count = 0
local proximal
local proximal_c = 0
for i2, v2 in pairs(v.stones) do for i2, v2 in pairs(v.stones) do
nodecore.set_loud(v2, {name = "nc_fire:fire"}) local under = check_position(v2 + down)
if (under:sub(1, 1) == "W") and (under:len() > 1) then
nodecore.set_loud(v2, {name = "nc_fire:fire"})
else
nodecore.set_loud(v2, {name = "air"})
end
count = count + 1
if (v2:distance(pos) < 2.3) then
proximal_c = proximal_c + 1
if math.random(1, proximal_c) == 1 then
proximal = v2
end
end
end end
-- todo: spawn itemstack of captured stones somehow local stack_max = minetest.registered_items[stone].stack_max
while count > 0 do
nodecore.item_eject(proximal, stone .. " " .. math.min(count, stack_max), 3)
count = count - stack_max
end
end end
end end
else else
local captures = check_captures(pos) local captures = check_captures(pos)
if captures.capture then if captures.capture then
minetest.chat_send_all("You gotcher self a suicide move right there! size: "..#(captures.stones)) local stone = minetest.get_node(pos).name
-- todo: replace with itemstack of one stone nodecore.set_loud(pos, {name = "air"})
nodecore.item_eject(pos, stone, 5)
end
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
end end
return itemstack
end 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,20 +68,79 @@ local function reg(name, basename, basedef)
sounds = basedef.sounds, 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)
minetest.register_node(":" .. territory_name, {
description = "Territory Marker",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-2/16, -8/16, -2/16, 2/16, -7/16, 2/16},
{-3/16, -7/16, -3/16, 3/16, -6/16, 3/16},
{-2/16, -6/16, -2/16, 2/16, -5/16, 2/16},
}
},
selection_box = {
type = "fixed",
fixed = {-6/16, -8/16, -6/16, 6/16, 0/16, 6/16}
},
tiles = {
basedef.tiles[1]
},
paramtype = "light",
sunlight_propagates = true,
groups = {
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({
label = "Chop scored cement into go stones",
action = "pummel",
toolgroups = {choppy = 3},
nodes = {
{
match = "nc_concrete:" .. name .. "_starcrossy_ply",
replace = "air"
}
},
items = {{name = piecename, count = 3, scatter = 4}},
}) })
nodecore.register_craft({ nodecore.register_craft({
label = "shave " .. string_lower(desc) .. " into stones", label = "Smash go stones into territory markers",
action = "pummel", action = "pummel",
toolgroups = {choppy = 3}, toolgroups = {thumpy = 3},
nodes = { nodes = {
{ {
match = "nc_concrete:" .. name .. "_starcrossy_ply", match = piecename,
replace = "air" replace = "air"
} }
}, },
items = {{name = piecename, count = 3, scatter = 4}}, items = {{name = territory_name, count = 9, scatter = 4}},
}) })
end end

Loading…
Cancel
Save