makeclass('player') makeclass('army') makeclass('orchard') -- -- HORPS game: Walk around the board collecting armies and buffs -- Captured armies eat each turn -- Attacking armies select order (random by default) -- Defending armies are stronger -- Command: Tame Creature Stack -- Command: Free Creature Stack -- -- -- Data Structure -- Army.Count[kind]=number Where kind is r,p,s -- Player.Count[kind]=number Where kind is r,p,s -- function player.interface(actor, place) gui.menu_item("cb_north" ,"Go North") gui.menu_item("cb_south" ,"Go South") gui.menu_item("cb_east" ,"Go East") gui.menu_item("cb_west" ,"Go West") gui.menu_item("cb_map" ,"Show the Map") gui.menu_item("cb_conjurerock" ,"Conjure Rock") gui.menu_item("cb_conjurepaper" ,"Conjure Paper") gui.menu_item("cb_conjurescissor" ,"Conjure Scissor") gui.menu_item("cb_droprock" ,"Drop Rock") gui.menu_item("cb_droppaper" ,"Drop Paper") gui.menu_item("cb_dropscissor" ,"Drop Scissor") end function player:cb_conjurerock() self.Count['r']=self.Count['r']+1 end function player:cb_conjurepaper() self.Count['p']=self.Count['p']+1 end function player:cb_conjurescissor() self.Count['s']=self.Count['s']+1 end function army.interface(actor,place) if place.owner==actor then gui_menu_item("cb_recruit","Recruit"); else gui.menu_item("cb_fight" ,"Fight"); end end -- Rock dulls Scissors 3 -- Rock cripples Lizard 2 -- Paper disproves Spock 3 -- Paper covers Rock 2 -- Scissors decapitates Lizard 3 -- Scissors cuts Paper 2 -- Lizard eats Paper 3 -- Lizard poisons Spock 2 -- Spock vaporizes Rock 3 -- Spock dismantles Scissors 2 armytypes={'r','p','s'} armynames={r="Rock Golem" ,p="Paper Dragon",s="Scissor Beast",l="Fire Lizzard",v="Mr. Spock"} foodnames={r="Raspberry" ,p="Pomegranite" ,s="Strawberry" ,l="Lemon" ,v="Mango" } --advantage={ r={ r=1, p=1/2, s=3, l=2, v=1/3 }, -- p={ r=2, p=1, s=1/2, l=1/3, v=3 }, -- s={ r=1/3, p=2, s=1, l=3, v=1/2 }, -- l={ r=1/2, p=3, s=1/3, l=1, v=2 }, -- v={ r=3, p=1/3, s=2, l=1/2, v=1 } } advantage={ r={ r=1, p=1/2, s=2 }, p={ r=2, p=1, s=1/2 }, s={ r=1/2, p=2, s=1 } } function shuffle(t) local s={} for i=1, #t do s[i]=t[i] end for i=#t,2,-1 do local j=math.random(i) s[i],s[j]=s[j],s[i] end return s end function qbuildarmy(x,y) t={class='army',x=x,y=y,z=0,plane='main',graphic='army'} local nt=tangible.build(t) nt.Count={} setmetatable(nt.Count,NilIsZero) nt.Count['r']=1 return nt end function MakeMap2() for i=-2,2 do qbuildarmy( i, 3) qbuildarmy( i,-3) qbuildarmy(-3, i) qbuildarmy( 3, i) end qbuildarmy(-3, 3) qbuildarmy( 3,-3) qbuildarmy(-3,-3) qbuildarmy( 3, 3) end function MakeMap() local rad=6 for x=-rad,rad do for y=-rad,rad do if math.random(1,5)==1 then t={class='army',x=x,y=y,z=0,plane='main',graphic='army'} -- print("Building an army:") -- pprint(t) local nt=tangible.build(t) nt.Count={} setmetatable(nt.Count,NilIsZero) for i=1,#armytypes do if math.random(1,2)==1 then local type=armytypes[math.random(1,#armytypes)] nt.Count[type]=nt.Count[type]+math.random(1,3) end end -- print("Here's what was built:") -- pprint(nt) end end end end -- -- For each creature type, select the optimal target. Select randomly among identical targets. -- function army.fight0(ak,ac,dk,dc,tweekdefender) -- returns number of attacker casualties, defender casualties local adv0=advantage[ak][dk] local adv1=advantage[dk][ak] if not tweekdefender then tweekdefender=function(adv) return adv end end adv1=tweekdefender(adv1) local losstotal=(ac+dc)/2 local power0=ac*adv0 local power1=dc*adv1 local powertotal=power0+power1 local loss0=math.min(ac,math.floor(losstotal*power1/powertotal)) local loss1=math.min(dc,math.floor(losstotal*power0/powertotal)) return loss0,loss1 end function player:fight(enemy) local rval={} for ak,ac in pairs(self.Count) do -- Should randomize the order local enemyk, enemyc local useloss0 local useloss1=-1 local loss0,loss1 local whichdk, whichdc for dk,dc in pairs(enemy.Count) do loss0,loss1=army.fight0(ak,ac,dk,dc) print("When "..ac.." "..ak.." fight "..dc.." "..dk.." they kill "..loss1.." and suffer "..loss0) if loss1>useloss1 then enemyk=dk enemyc=dc useloss1=loss1 useloss0=loss0 end end if enemyk then print("Army of "..ac.." "..ak.." fights "..enemyc.." "..enemyk..", killing "..useloss1.." and suffering "..useloss0) rval[1+#rval]={'fight',ak,ac,useloss0,enemyk,enemyc,useloss1} enemy.Count[enemyk]=enemy.Count[enemyk]-useloss1 self.Count[ak]=self.Count[ak]-useloss0 end end local enemyleft=0 local playerleft=0 for dk,dc in pairs(enemy.Count) do enemyleft=enemyleft+dc end for ak,ac in pairs(self.Count) do playerleft=playerleft+ac end rval.enemyleft=enemyleft return rval end function player:cb_droprock(actor) player:droparmy(actor,'r') end function player:cb_droppaper(actor) player:droparmy(actor,'p') end function player:cb_dropscissor(actor) player:droparmy(actor,'s') end function player:droparmy(actor,kind) if actor.Count[kind]<=0 then print("No armies of type "..kind.." available") return end local lis=tangible.near(actor,0,true,true) if #lis>1 then print("Multiple Tangibles Nearby") return end local t=lis[1] if t==nil then t=player:buildarmy(actor) end t.Count[kind]=t.Count[kind]+1 pprint(t) end function orchard:gather(actor) actor.food[self.kind]=actor.inventory[self.kind]+self.count end function player:newlocation() local lis=tangible.near(self,0,true,true) local count={} setmetatable(count,NilIsZero) local count0=0 local result={} for _,t in ipairs(lis) do if tangible.getclass(t)=='army' then result[1+#result]=self:fight(t) end end if #result==1 and result[1].enemyleft>0 then for _,t in ipairs(lis) do if tangible.getclass(t)=='orchard' then t:gather(self) end end end return result end function player:printanimstate() local graphic,plane,x,y,z,facing = tangible.animstate(self) print("Resulting state: ", graphic, plane, x, y, z, facing) end function player:walk(dx,dy) tangible.animate(self,{action='walk',dx=dx,dy=dy}) local result=self:newlocation() pprint(result) if #result>1 then print("Error: multiple armies at one location") elseif #result==1 then if result[1].enemyleft>0 then tangible.animate(self,{action='walk',dx=-dx,dy=-dy}) end end -- self:cb_map() if #result==1 then for i,v in ipairs(result[1]) do if v[1]=='fight' then print(v[3]..v[2].." attacks "..v[6]..v[5]..", killing "..v[7].." while losing "..v[4]) end end end end function player:cb_north() self:walk(0,1) end function player:cb_south() self:walk(0,-1) end function player:cb_east() self:walk(1,0) end function player:cb_west() self:walk(0,1) end function player:buildarmy(actor) local _,pl,ax,ay=tangible.animstate(actor) t={class='army',x=ax,y=ay,z=0,plane=pl,graphic='army'} local nt=tangible.build(t) nt.Count={} setmetatable(nt.Count,{__index=function(t,k) return 0 end,__newindex=function(t,k,v) if v~=nil and v~=0 then rawset(t,k,v) end end}) return nt end function player.cb_emit_army(actor,place,dialog) local _,pl,ax,ay=tangible.animstate(actor) t={class='army',x=ax,y=ay,z=0,plane=pl,graphic='army'} pprint("building ",t) local nt=tangible.build(t) setmetatable(nt,{r=0,p=0,s=0}) nt.kind='r' nt.Count=1 end function player.cb_emit_buff(actor,place,dialog) local _,pl,ax,ay=tangible.animstate(actor) t={class='army',x=ax,y=ay,z=0,plane=pl,graphic='buff'} pprint("building ",t) tangible.build(t) end makeclass('army') makeclass('buff') function seq(a,b,c) return a<=b and b<=c or false end function num2(a) if a<=9 then return " "..a else return a end end function player.cb_map(actor,place,dialog) local rad=4 local ax,ay=tangible.xyz(actor) local scratch={} local lis=tangible.near(actor,1.5*rad,true,false) print("\27c\27[38;5;9mMap:\27[0m") for _,t in pairs(lis) do -- local graphic,plane,x,y,z,facing = tangible.animstate(t) local c=tangible.getclass(t) local dx,dy=tangible.xyz(t) local offset=(-dy+ay+rad)*(rad*2+1)+dx-ax+rad local cl=tangible.getclass(t) if seq(ax-rad,dx,ax+rad) and seq(ay-rad,dy,ay+rad) and (cl=='player' or cl=='army') then if not scratch[offset] then scratch[offset]={} scratch[offset].Count={} setmetatable(scratch[offset].Count,NilIsZero) end if t.Count then for i,k in ipairs(armytypes) do local a0=scratch[offset] local a1=a0.Count local a2=a1[i] local a3=t.Count local a4=a3[k] if not a4 then print("a4 is nil") pprint(t) end scratch[offset].Count[i]=a2+a4 end end end end -- pprint(scratch) for dy=-rad,rad do for line=0,1 do local lbuf="|" for dx=-rad,rad do local offset=(dy+rad)*(rad*2+1)+dx+rad if line==0 then lbuf=lbuf.."----" elseif line==1 then if scratch[offset] then for i,k in ipairs(armytypes) do lbuf=lbuf..scratch[offset].Count[i] end else for i=1,#armytypes do lbuf=lbuf..' ' end end lbuf=lbuf.."-" end end print(lbuf) end end lbuf="" for dx=-rad,rad do lbuf=lbuf.."----" end lbuf=lbuf.."-" print(lbuf) -- print("In Player "..tangible.id(actor)..":") -- for k,v in pairs(actor.Count) do print(" "..k.." "..v) end -- local lis=tangible.near(actor,0,true,true) -- for k,v in pairs(lis) do -- print("In Army "..tangible.id(v)..":") -- for k2,v2 in pairs(v.Count) do print(" "..k2.." "..v2) end -- end end