重构ai模块,等待使用新wrapper接入cc,bot数据结构未定
This commit is contained in:
2
main.lua
2
main.lua
@@ -82,7 +82,7 @@ SKIN= require'parts.skin'
|
|||||||
USERS= require'parts.users'
|
USERS= require'parts.users'
|
||||||
NET= require'parts.net'
|
NET= require'parts.net'
|
||||||
VK= require'parts.virtualKey'
|
VK= require'parts.virtualKey'
|
||||||
AIFUNC= require'parts.ai'
|
BOT= require'parts.bot'
|
||||||
AIBUILDER= require'parts.AITemplate'
|
AIBUILDER= require'parts.AITemplate'
|
||||||
RSlist= require'parts.RSlist'DSCP=RSlist.TRS.centerPos
|
RSlist= require'parts.RSlist'DSCP=RSlist.TRS.centerPos
|
||||||
PLY= require'parts.player'
|
PLY= require'parts.player'
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ return function(type,speedLV,next,hold,node)
|
|||||||
type='CC',
|
type='CC',
|
||||||
next=next,
|
next=next,
|
||||||
hold=hold,
|
hold=hold,
|
||||||
delta=AISpeed[speedLV],
|
delay=AISpeed[speedLV],
|
||||||
node=node,
|
node=node,
|
||||||
}
|
}
|
||||||
elseif type=='9S'then
|
elseif type=='9S'then
|
||||||
return{
|
return{
|
||||||
type='9S',
|
type='9S',
|
||||||
delta=math.floor(AISpeed[speedLV]),
|
delay=math.floor(AISpeed[speedLV]),
|
||||||
hold=hold,
|
hold=hold,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
339
parts/ai.lua
339
parts/ai.lua
@@ -1,339 +0,0 @@
|
|||||||
local int,ceil,min,abs,rnd,modf=math.floor,math.ceil,math.min,math.abs,math.random,math.modf
|
|
||||||
local ins,rem=table.insert,table.remove
|
|
||||||
local yield=coroutine.yield
|
|
||||||
-- controlname:
|
|
||||||
-- 1~5:mL,mR,rR,rL,rF,
|
|
||||||
-- 6~10:hD,sD,H,A,R,
|
|
||||||
-- 11~13:LL,RR,DD
|
|
||||||
local blockPos={4,4,4,4,4,5,4}
|
|
||||||
-------------------------------------------------Cold clear
|
|
||||||
local _CC=LOADLIB('CC',{
|
|
||||||
Windows="CCloader",
|
|
||||||
Linux="CCloader",
|
|
||||||
Android="libCCloader.so",
|
|
||||||
["OS X"]="CCloader.dylib",
|
|
||||||
libFunc="luaopen_CCloader",
|
|
||||||
})cc=nil
|
|
||||||
if type(_CC)=='table'then
|
|
||||||
local CCblockID={6,5,4,3,2,1,0}
|
|
||||||
CC={
|
|
||||||
getConf= _CC.get_default_config ,--()options,weights
|
|
||||||
fastWeights=_CC.fast_weights ,--(weights)
|
|
||||||
--setConf= _CC.set_options ,--(options,hold,20g,bag7)
|
|
||||||
|
|
||||||
new= _CC.launch_async ,--(options,weights)bot
|
|
||||||
addNext= function(bot,id)_CC.add_next_piece_async(bot,CCblockID[id])end ,--(bot,piece)
|
|
||||||
update= _CC.reset_async ,--(bot,field,b2b,combo)
|
|
||||||
think= _CC.request_next_move ,--(bot)
|
|
||||||
getMove= _CC.poll_next_move ,--(bot)success,result,dest,hold,move
|
|
||||||
destroy= _CC.destroy_async ,--(bot)
|
|
||||||
|
|
||||||
setHold= _CC.set_hold ,--(opt,bool)
|
|
||||||
set20G= _CC.set_20g ,--(opt,bool)
|
|
||||||
-- setPCLoop= _CC.set_pcloop ,--(opt,bool)
|
|
||||||
setBag= _CC.set_bag7 ,--(opt,bool)
|
|
||||||
setNode= _CC.set_max_nodes ,--(opt,bool)
|
|
||||||
free= _CC.free ,--(opt/wei)
|
|
||||||
}
|
|
||||||
local CC=CC
|
|
||||||
function CC.updateField(P)
|
|
||||||
if not P.AI_thread then return end
|
|
||||||
local F,n={},1
|
|
||||||
local field=P.field
|
|
||||||
for y=1,min(#field,40)do
|
|
||||||
local sum=0
|
|
||||||
for x=1,10 do
|
|
||||||
if field[y][x]>0 then
|
|
||||||
F[n]=true
|
|
||||||
sum=sum+1
|
|
||||||
else
|
|
||||||
F[n]=false
|
|
||||||
end
|
|
||||||
n=n+1
|
|
||||||
end
|
|
||||||
if sum==10 then
|
|
||||||
--[[
|
|
||||||
--Print field and crash the game
|
|
||||||
for i=24,0,-1 do
|
|
||||||
local l=""for j=1,10 do l=l..(F[10*i+j]and"X"or"_")end print(l)
|
|
||||||
end
|
|
||||||
error("Row "..y.." full")
|
|
||||||
]]
|
|
||||||
P:destroyBot()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while n<=400 do
|
|
||||||
F[n]=false
|
|
||||||
n=n+1
|
|
||||||
end
|
|
||||||
if not pcall(CC.update,P.AI_bot,F,P.b2b>=100,P.combo)then
|
|
||||||
P:destroyBot()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function CC.switch20G(P)
|
|
||||||
P:destroyBot()
|
|
||||||
P.AIdata._20G=true
|
|
||||||
P.AI_keys={}
|
|
||||||
local opt,wei=CC.getConf()
|
|
||||||
CC.fastWeights(wei)
|
|
||||||
CC.setHold(opt,P.AIdata.hold)
|
|
||||||
CC.set20G(opt,P.AIdata._20G)
|
|
||||||
CC.setBag(opt,P.AIdata.bag=='bag')
|
|
||||||
CC.setNode(opt,P.AIdata.node)
|
|
||||||
P.AI_bot=CC.new(opt,wei)
|
|
||||||
CC.free(opt)CC.free(wei)
|
|
||||||
for i=1,P.AIdata.next do
|
|
||||||
CC.addNext(P.AI_bot,CCblockID[P.nextQueue[i].id])
|
|
||||||
end
|
|
||||||
CC.updateField(P)
|
|
||||||
|
|
||||||
TABLE.cut(P.holdQueue)
|
|
||||||
P.holdTime=P.gameEnv.holdCount
|
|
||||||
|
|
||||||
local C=rem(P.nextQueue,1)
|
|
||||||
P.cur=C
|
|
||||||
P.curX,P.curY=blockPos[C.id],int(P.gameEnv.fieldH+1-modf(C.centerPos[C.id][C.dir][1]))+ceil(P.fieldBeneath/30)
|
|
||||||
|
|
||||||
P.newNext()
|
|
||||||
local id=CCblockID[P.nextQueue[P.AIdata.next].id]
|
|
||||||
if id then
|
|
||||||
CC.addNext(P.AI_bot,id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-------------------------------------------------9 Stack setup
|
|
||||||
--[[Future:
|
|
||||||
HighestBlock
|
|
||||||
BlockedCells
|
|
||||||
Wells
|
|
||||||
FilledLines
|
|
||||||
4deepShape
|
|
||||||
BlockedWells
|
|
||||||
]]
|
|
||||||
local dirCount={1,1,3,3,3,0,1}
|
|
||||||
local FCL={
|
|
||||||
[1]={
|
|
||||||
{{11},{11,2},{1},{},{2},{2,2},{12,1},{12}},
|
|
||||||
{{11,4},{11,3},{1,4},{4},{3},{2,3},{2,2,3},{12,4},{12,3}},
|
|
||||||
},
|
|
||||||
[3]={
|
|
||||||
{{11},{11,2},{1},{},{2},{2,2},{12,1},{12}},
|
|
||||||
{{3,11},{11,3},{11,2,3},{1,3},{3},{2,3},{2,2,3},{12,1,3},{12,3}},
|
|
||||||
{{11,5},{11,2,5},{1,5},{5},{2,5},{2,2,5},{12,1,5},{12,5}},
|
|
||||||
{{11,4},{11,2,4},{1,4},{4},{2,4},{2,2,4},{12,1,4},{12,4},{4,12}},
|
|
||||||
},
|
|
||||||
[6]={
|
|
||||||
{{11},{11,2},{1,1},{1},{},{2},{2,2},{12,1},{12}},
|
|
||||||
},
|
|
||||||
[7]={
|
|
||||||
{{11},{11,2},{1},{},{2},{12,1},{12}},
|
|
||||||
{{4,11},{11,4},{11,3},{1,4},{4},{3},{2,3},{12,4},{12,3},{3,12}},
|
|
||||||
},
|
|
||||||
}FCL[2],FCL[4],FCL[5]=FCL[1],FCL[3],FCL[3]
|
|
||||||
local LclearScore={[0]=0,-200,-120,-80,200}
|
|
||||||
local HclearScore={[0]=0,100,140,200,500}
|
|
||||||
local function ifoverlapAI(f,bk,x,y)
|
|
||||||
for i=1,#bk do for j=1,#bk[1]do
|
|
||||||
if f[y+i-1]and bk[i][j]and f[y+i-1][x+j-1]>0 then return true end
|
|
||||||
end end
|
|
||||||
end
|
|
||||||
local discardRow=FREEROW.discard
|
|
||||||
local getRow=FREEROW.get
|
|
||||||
local function resetField(f0,f,start)
|
|
||||||
for _=#f,start,-1 do
|
|
||||||
discardRow(f[_])
|
|
||||||
f[_]=nil
|
|
||||||
end
|
|
||||||
for i=start,#f0 do
|
|
||||||
f[i]=getRow(0)
|
|
||||||
for j=1,10 do
|
|
||||||
f[i][j]=f0[i][j]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local function getScore(field,cb,cy)
|
|
||||||
local score=0
|
|
||||||
local highest=0
|
|
||||||
local height=getRow(0)
|
|
||||||
local clear=0
|
|
||||||
local hole=0
|
|
||||||
|
|
||||||
for i=cy+#cb-1,cy,-1 do
|
|
||||||
for j=1,10 do
|
|
||||||
if field[i][j]==0 then goto CONTINUE_notFull end
|
|
||||||
end
|
|
||||||
discardRow(rem(field,i))
|
|
||||||
clear=clear+1
|
|
||||||
::CONTINUE_notFull::
|
|
||||||
end
|
|
||||||
if #field==0 then return 1e99 end--PC
|
|
||||||
for x=1,10 do
|
|
||||||
local h=#field
|
|
||||||
while field[h][x]==0 and h>1 do
|
|
||||||
h=h-1
|
|
||||||
end
|
|
||||||
height[x]=h
|
|
||||||
if x>3 and x<8 and h>highest then highest=h end
|
|
||||||
if h>1 then
|
|
||||||
for h1=h-1,1,-1 do
|
|
||||||
if field[h1][x]==0 then
|
|
||||||
hole=hole+1
|
|
||||||
if hole==5 then break end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local sdh=0
|
|
||||||
local h1,mh1=0,0
|
|
||||||
for x=1,9 do
|
|
||||||
local dh=abs(height[x]-height[x+1])
|
|
||||||
if dh==1 then
|
|
||||||
h1=h1+1
|
|
||||||
if h1>mh1 then mh1=h1 end
|
|
||||||
else
|
|
||||||
h1=0
|
|
||||||
end
|
|
||||||
sdh=sdh+min(dh^1.6,20)
|
|
||||||
end
|
|
||||||
discardRow(height)
|
|
||||||
score=
|
|
||||||
-#field*30
|
|
||||||
-#cb*15
|
|
||||||
+(#field>10 and
|
|
||||||
HclearScore[clear]--Clearing
|
|
||||||
-hole*70--Hole
|
|
||||||
-cy*50--Height
|
|
||||||
-sdh--Sum of DeltaH
|
|
||||||
or
|
|
||||||
LclearScore[clear]
|
|
||||||
-hole*100
|
|
||||||
-cy*40
|
|
||||||
-sdh*3
|
|
||||||
)
|
|
||||||
if #field>6 then score=score-highest*5+20 end
|
|
||||||
if mh1>3 then score=score-20-mh1*30 end
|
|
||||||
return score
|
|
||||||
end
|
|
||||||
-------------------------------------------------
|
|
||||||
local BLOCKS=BLOCKS
|
|
||||||
local CC=CC
|
|
||||||
return{
|
|
||||||
['9S']=function(P,keys)
|
|
||||||
while true do
|
|
||||||
--Thinking
|
|
||||||
yield()
|
|
||||||
local Tfield={}--Test field
|
|
||||||
local best={x=1,dir=0,hold=false,score=-1e99}--Best method
|
|
||||||
local field_org=P.field
|
|
||||||
for i=1,#field_org do
|
|
||||||
Tfield[i]=getRow(0)
|
|
||||||
for j=1,10 do
|
|
||||||
Tfield[i][j]=field_org[i][j]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for ifhold=0,P.gameEnv.holdCount>0 and 1 or 0 do
|
|
||||||
--Get block id
|
|
||||||
local bn
|
|
||||||
if ifhold==0 then
|
|
||||||
bn=P.cur and P.cur.id
|
|
||||||
else
|
|
||||||
bn=P.holdQueue[1]and P.holdQueue[1].id or P.nextQueue[1]and P.nextQueue[1].id
|
|
||||||
end
|
|
||||||
if bn then
|
|
||||||
for dir=0,dirCount[bn]do--Each dir
|
|
||||||
local cb=BLOCKS[bn][dir]
|
|
||||||
for cx=1,11-#cb[1]do--Each pos
|
|
||||||
local cy=#Tfield+1
|
|
||||||
|
|
||||||
--Move to bottom
|
|
||||||
while cy>1 and not ifoverlapAI(Tfield,cb,cx,cy-1)do
|
|
||||||
cy=cy-1
|
|
||||||
end
|
|
||||||
|
|
||||||
--Simulate lock
|
|
||||||
for i=1,#cb do
|
|
||||||
local y=cy+i-1
|
|
||||||
if not Tfield[y]then Tfield[y]=getRow(0)end
|
|
||||||
local L=Tfield[y]
|
|
||||||
for j=1,#cb[1]do
|
|
||||||
if cb[i][j]then
|
|
||||||
L[cx+j-1]=1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local score=getScore(Tfield,cb,cy)
|
|
||||||
if score>best.score then
|
|
||||||
best={bn=bn,x=cx,dir=dir,hold=ifhold==1,score=score}
|
|
||||||
end
|
|
||||||
resetField(field_org,Tfield,cy)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not best.bn then return 1 end
|
|
||||||
|
|
||||||
--Release cache
|
|
||||||
while #Tfield>0 do
|
|
||||||
discardRow(rem(Tfield,1))
|
|
||||||
end
|
|
||||||
if best.hold then
|
|
||||||
ins(keys,8)
|
|
||||||
end
|
|
||||||
local l=FCL[best.bn][best.dir+1][best.x]
|
|
||||||
for i=1,#l do
|
|
||||||
ins(keys,l[i])
|
|
||||||
end
|
|
||||||
ins(keys,6)
|
|
||||||
|
|
||||||
--Check if time to change target
|
|
||||||
yield()
|
|
||||||
if P.aiRND:random()<.00126 then
|
|
||||||
P:changeAtkMode(rnd()<.85 and 1 or #P.atker>3 and 4 or rnd()<.3 and 2 or 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
['CC']=CC and function(P,keys)
|
|
||||||
while true do
|
|
||||||
--Start thinking
|
|
||||||
yield()
|
|
||||||
if not P.AI_bot then break end
|
|
||||||
if not pcall(CC.think,P.AI_bot)then break end
|
|
||||||
|
|
||||||
--Poll keys
|
|
||||||
local success,result,dest,hold,move
|
|
||||||
repeat
|
|
||||||
yield()
|
|
||||||
if not P.AI_bot then break end
|
|
||||||
success,result,dest,hold,move=pcall(CC.getMove,P.AI_bot)
|
|
||||||
until not success or result==0 or result==2
|
|
||||||
if not success then break end
|
|
||||||
if result==2 then
|
|
||||||
break
|
|
||||||
elseif result==0 then
|
|
||||||
dest[5],dest[6]=dest[1][1],dest[1][2]
|
|
||||||
dest[7],dest[8]=dest[2][1],dest[2][2]
|
|
||||||
dest[1],dest[2]=dest[3][1],dest[3][2]
|
|
||||||
dest[3],dest[4]=dest[4][1],dest[4][2]
|
|
||||||
P.AI_dest=dest
|
|
||||||
if hold then keys[1]=8 end--Hold
|
|
||||||
while move[1]do
|
|
||||||
local m=rem(move,1)
|
|
||||||
if m<4 then
|
|
||||||
ins(keys,m+1)
|
|
||||||
elseif not P.AIdata._20G then
|
|
||||||
ins(keys,13)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
ins(keys,6)
|
|
||||||
end
|
|
||||||
|
|
||||||
--Check if time to change target
|
|
||||||
yield()
|
|
||||||
if P.aiRND:random()<.00126 then
|
|
||||||
P:changeAtkMode(rnd()<.85 and 1 or #P.atker>3 and 4 or rnd()<.3 and 2 or 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}--AI brains
|
|
||||||
199
parts/bot/bot_9s.lua
Normal file
199
parts/bot/bot_9s.lua
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
--[[ControlID:
|
||||||
|
1~5:mL,mR,rR,rL,rF,
|
||||||
|
6~10:hD,sD,H,A,R,
|
||||||
|
11~13:LL,RR,DD
|
||||||
|
]]
|
||||||
|
--[[Future:
|
||||||
|
HighestBlock
|
||||||
|
BlockedCells
|
||||||
|
Wells
|
||||||
|
FilledLines
|
||||||
|
4deepShape
|
||||||
|
BlockedWells
|
||||||
|
]]
|
||||||
|
local min,abs,rnd=math.min,math.abs,math.random
|
||||||
|
local ins,rem=table.insert,table.remove
|
||||||
|
local yield=coroutine.yield
|
||||||
|
|
||||||
|
local dirCount={1,1,3,3,3,0,1}
|
||||||
|
local FCL={
|
||||||
|
[1]={
|
||||||
|
{{11},{11,2},{1},{},{2},{2,2},{12,1},{12}},
|
||||||
|
{{11,4},{11,3},{1,4},{4},{3},{2,3},{2,2,3},{12,4},{12,3}},
|
||||||
|
},
|
||||||
|
[3]={
|
||||||
|
{{11},{11,2},{1},{},{2},{2,2},{12,1},{12}},
|
||||||
|
{{3,11},{11,3},{11,2,3},{1,3},{3},{2,3},{2,2,3},{12,1,3},{12,3}},
|
||||||
|
{{11,5},{11,2,5},{1,5},{5},{2,5},{2,2,5},{12,1,5},{12,5}},
|
||||||
|
{{11,4},{11,2,4},{1,4},{4},{2,4},{2,2,4},{12,1,4},{12,4},{4,12}},
|
||||||
|
},
|
||||||
|
[6]={
|
||||||
|
{{11},{11,2},{1,1},{1},{},{2},{2,2},{12,1},{12}},
|
||||||
|
},
|
||||||
|
[7]={
|
||||||
|
{{11},{11,2},{1},{},{2},{12,1},{12}},
|
||||||
|
{{4,11},{11,4},{11,3},{1,4},{4},{3},{2,3},{12,4},{12,3},{3,12}},
|
||||||
|
},
|
||||||
|
}FCL[2],FCL[4],FCL[5]=FCL[1],FCL[3],FCL[3]
|
||||||
|
local LclearScore={[0]=0,-200,-150,-100,200}
|
||||||
|
local HclearScore={[0]=0,100,140,200,500}
|
||||||
|
local function ifoverlapAI(f,bk,x,y)
|
||||||
|
for i=1,#bk do for j=1,#bk[1]do
|
||||||
|
if f[y+i-1]and bk[i][j]and f[y+i-1][x+j-1]>0 then return true end
|
||||||
|
end end
|
||||||
|
end
|
||||||
|
local discardRow=FREEROW.discard
|
||||||
|
local getRow=FREEROW.get
|
||||||
|
local function resetField(f0,f,start)
|
||||||
|
for _=#f,start,-1 do
|
||||||
|
discardRow(f[_])
|
||||||
|
f[_]=nil
|
||||||
|
end
|
||||||
|
for i=start,#f0 do
|
||||||
|
f[i]=getRow(0)
|
||||||
|
for j=1,10 do
|
||||||
|
f[i][j]=f0[i][j]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function getScore(field,cb,cy)
|
||||||
|
local score=0
|
||||||
|
local highest=0
|
||||||
|
local height=getRow(0)
|
||||||
|
local clear=0
|
||||||
|
local hole=0
|
||||||
|
|
||||||
|
for i=cy+#cb-1,cy,-1 do
|
||||||
|
for j=1,10 do
|
||||||
|
if field[i][j]==0 then goto CONTINUE_notFull end
|
||||||
|
end
|
||||||
|
discardRow(rem(field,i))
|
||||||
|
clear=clear+1
|
||||||
|
::CONTINUE_notFull::
|
||||||
|
end
|
||||||
|
if #field==0 then return 1e99 end--PC
|
||||||
|
for x=1,10 do
|
||||||
|
local h=#field
|
||||||
|
while field[h][x]==0 and h>1 do
|
||||||
|
h=h-1
|
||||||
|
end
|
||||||
|
height[x]=h
|
||||||
|
if x>3 and x<8 and h>highest then highest=h end
|
||||||
|
if h>1 then
|
||||||
|
for h1=h-1,1,-1 do
|
||||||
|
if field[h1][x]==0 then
|
||||||
|
hole=hole+1
|
||||||
|
if hole==5 then break end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local sdh=0
|
||||||
|
local h1,mh1=0,0
|
||||||
|
for x=1,9 do
|
||||||
|
local dh=abs(height[x]-height[x+1])
|
||||||
|
if dh==1 then
|
||||||
|
h1=h1+1
|
||||||
|
if h1>mh1 then mh1=h1 end
|
||||||
|
else
|
||||||
|
h1=0
|
||||||
|
end
|
||||||
|
sdh=sdh+min(dh^1.6,20)
|
||||||
|
end
|
||||||
|
discardRow(height)
|
||||||
|
score=
|
||||||
|
-#field*30
|
||||||
|
-#cb*15
|
||||||
|
+(#field>10 and
|
||||||
|
HclearScore[clear]--Clearing
|
||||||
|
-hole*70--Hole
|
||||||
|
-cy*50--Height
|
||||||
|
-sdh--Sum of DeltaH
|
||||||
|
or
|
||||||
|
LclearScore[clear]
|
||||||
|
-hole*100
|
||||||
|
-cy*40
|
||||||
|
-sdh*3
|
||||||
|
)
|
||||||
|
if #field>6 then score=score-highest*5+20 end
|
||||||
|
if mh1>3 then score=score-20-mh1*30 end
|
||||||
|
return score
|
||||||
|
end
|
||||||
|
|
||||||
|
local bot_9s={}
|
||||||
|
function bot_9s.thread(P,keys)
|
||||||
|
while true do
|
||||||
|
--Thinking
|
||||||
|
yield()
|
||||||
|
local Tfield={}--Test field
|
||||||
|
local best={x=1,dir=0,hold=false,score=-1e99}--Best method
|
||||||
|
local field_org=P.field
|
||||||
|
for i=1,#field_org do
|
||||||
|
Tfield[i]=getRow(0)
|
||||||
|
for j=1,10 do
|
||||||
|
Tfield[i][j]=field_org[i][j]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for ifhold=0,P.gameEnv.holdCount>0 and 1 or 0 do
|
||||||
|
--Get block id
|
||||||
|
local bn
|
||||||
|
if ifhold==0 then
|
||||||
|
bn=P.cur and P.cur.id
|
||||||
|
else
|
||||||
|
bn=P.holdQueue[1]and P.holdQueue[1].id or P.nextQueue[1]and P.nextQueue[1].id
|
||||||
|
end
|
||||||
|
if bn then
|
||||||
|
for dir=0,dirCount[bn]do--Each dir
|
||||||
|
local cb=BLOCKS[bn][dir]
|
||||||
|
for cx=1,11-#cb[1]do--Each pos
|
||||||
|
local cy=#Tfield+1
|
||||||
|
|
||||||
|
--Move to bottom
|
||||||
|
while cy>1 and not ifoverlapAI(Tfield,cb,cx,cy-1)do
|
||||||
|
cy=cy-1
|
||||||
|
end
|
||||||
|
|
||||||
|
--Simulate lock
|
||||||
|
for i=1,#cb do
|
||||||
|
local y=cy+i-1
|
||||||
|
if not Tfield[y]then Tfield[y]=getRow(0)end
|
||||||
|
local L=Tfield[y]
|
||||||
|
for j=1,#cb[1]do
|
||||||
|
if cb[i][j]then
|
||||||
|
L[cx+j-1]=1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local score=getScore(Tfield,cb,cy)
|
||||||
|
if score>best.score then
|
||||||
|
best={bn=bn,x=cx,dir=dir,hold=ifhold==1,score=score}
|
||||||
|
end
|
||||||
|
resetField(field_org,Tfield,cy)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not best.bn then return 1 end
|
||||||
|
|
||||||
|
--Release cache
|
||||||
|
while #Tfield>0 do
|
||||||
|
discardRow(rem(Tfield,1))
|
||||||
|
end
|
||||||
|
if best.hold then
|
||||||
|
ins(keys,8)
|
||||||
|
end
|
||||||
|
local l=FCL[best.bn][best.dir+1][best.x]
|
||||||
|
for i=1,#l do
|
||||||
|
ins(keys,l[i])
|
||||||
|
end
|
||||||
|
ins(keys,6)
|
||||||
|
|
||||||
|
--Check if time to change target
|
||||||
|
yield()
|
||||||
|
if P.aiRND:random()<.00126 then
|
||||||
|
P:changeAtkMode(rnd()<.85 and 1 or #P.atker>3 and 4 or rnd()<.3 and 2 or 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return bot_9s
|
||||||
56
parts/bot/init.lua
Normal file
56
parts/bot/init.lua
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
local rem=table.remove
|
||||||
|
|
||||||
|
local baseBot={
|
||||||
|
pushNewNext=NULL,
|
||||||
|
updateField=NULL,
|
||||||
|
lockWrongPlace=NULL,
|
||||||
|
switch20G=NULL,
|
||||||
|
revive=NULL,
|
||||||
|
}
|
||||||
|
function baseBot.update(bot)
|
||||||
|
local P=bot.P
|
||||||
|
if P.control and P.waiting==-1 then
|
||||||
|
local keyQueue=bot.keys
|
||||||
|
bot.delay=bot.delay-1
|
||||||
|
if not keyQueue[1]then
|
||||||
|
if bot.runningThread then
|
||||||
|
pcall(bot.runningThread)
|
||||||
|
if not pcall(bot.runningThread)then
|
||||||
|
P:destroyBot()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
P:act_hardDrop()
|
||||||
|
end
|
||||||
|
elseif bot.delay<=0 then
|
||||||
|
bot.delay=bot.delay0*.5
|
||||||
|
P:pressKey(keyQueue[1])P:releaseKey(keyQueue[1])
|
||||||
|
rem(keyQueue,1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local botMeta={__index=function(self,k)
|
||||||
|
MES.new('warn',"Undefined method: "..k)
|
||||||
|
self[k]=NULL
|
||||||
|
return NULL
|
||||||
|
end}
|
||||||
|
|
||||||
|
return{
|
||||||
|
new=function(P,data)
|
||||||
|
local bot={P=P}
|
||||||
|
if data.type=="/"then
|
||||||
|
--
|
||||||
|
else
|
||||||
|
TABLE.cover(baseBot,bot)
|
||||||
|
TABLE.cover(require"parts.bot.bot_9s",bot)
|
||||||
|
bot.P:setRS('TRS')
|
||||||
|
bot.runningThread=coroutine.wrap(bot.thread)
|
||||||
|
bot.keys={}
|
||||||
|
bot.delay=data.delay
|
||||||
|
bot.delay0=data.delay
|
||||||
|
bot.runningThread(P,bot.keys)
|
||||||
|
setmetatable(bot,botMeta)
|
||||||
|
end
|
||||||
|
return bot
|
||||||
|
end
|
||||||
|
}
|
||||||
@@ -207,7 +207,6 @@ function destroyPlayers()--Destroy all player objects, restore freerows and free
|
|||||||
FREEROW.discard(rem(P.field))
|
FREEROW.discard(rem(P.field))
|
||||||
FREEROW.discard(rem(P.visTime))
|
FREEROW.discard(rem(P.visTime))
|
||||||
end
|
end
|
||||||
P:destroyBot()
|
|
||||||
end
|
end
|
||||||
TABLE.cut(PLAYERS)
|
TABLE.cut(PLAYERS)
|
||||||
TABLE.cut(PLY_ALIVE)
|
TABLE.cut(PLY_ALIVE)
|
||||||
|
|||||||
@@ -801,8 +801,8 @@ function draw.norm(P,repMode)
|
|||||||
|
|
||||||
--Draw AI's drop destination
|
--Draw AI's drop destination
|
||||||
if P.AI_dest then
|
if P.AI_dest then
|
||||||
local texture=TEXTURE.puzzleMark[21]
|
|
||||||
local L=P.AI_dest
|
local L=P.AI_dest
|
||||||
|
local texture=TEXTURE.puzzleMark[21]
|
||||||
for i=1,#L,2 do
|
for i=1,#L,2 do
|
||||||
gc_draw(texture,30*L[i],-30*L[i+1]-30)
|
gc_draw(texture,30*L[i],-30*L[i+1]-30)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -379,8 +379,7 @@ function PLY.newDemoPlayer(id)
|
|||||||
type='CC',
|
type='CC',
|
||||||
next=5,
|
next=5,
|
||||||
hold=true,
|
hold=true,
|
||||||
delay=30,
|
delay=6,
|
||||||
delta=6,
|
|
||||||
bag='bag',
|
bag='bag',
|
||||||
node=100000,
|
node=100000,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ function Player:set20G(if20g)
|
|||||||
self:switchKey(14,not if20g)
|
self:switchKey(14,not if20g)
|
||||||
self:switchKey(15,not if20g)
|
self:switchKey(15,not if20g)
|
||||||
self:switchKey(16,not if20g)
|
self:switchKey(16,not if20g)
|
||||||
if if20g and self.AI_mode=='CC'then CC.switch20G(self)end
|
if if20g and self.bot then self.bot:switch20G()end
|
||||||
end
|
end
|
||||||
function Player:setHold(count)--Set hold count (false/true as 0/1)
|
function Player:setHold(count)--Set hold count (false/true as 0/1)
|
||||||
if not count then
|
if not count then
|
||||||
@@ -240,15 +240,6 @@ function Player:setRS(RSname)
|
|||||||
for i=1,#self.holdQueue do self.holdQueue[i].rs=rs end
|
for i=1,#self.holdQueue do self.holdQueue[i].rs=rs end
|
||||||
if self.cur then self.cur.rs=rs end
|
if self.cur then self.cur.rs=rs end
|
||||||
end
|
end
|
||||||
function Player:destroyBot()
|
|
||||||
if self.AI_mode=='CC'then
|
|
||||||
if self.AI_bot then
|
|
||||||
CC.destroy(self.AI_bot)
|
|
||||||
self.AI_bot=nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.AI_thread=nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function Player:getHolePos()--Get a good garbage-line hole position
|
function Player:getHolePos()--Get a good garbage-line hole position
|
||||||
if self.garbageBeneath==0 then
|
if self.garbageBeneath==0 then
|
||||||
@@ -276,7 +267,7 @@ function Player:garbageRelease()--Check garbage buffer and try to release them
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if flag and self.AI_mode=='CC'then CC.updateField(self)end
|
if flag and self.bot then self.bot:updateField()end
|
||||||
end
|
end
|
||||||
function Player:garbageRise(color,amount,line)--Release n-lines garbage to field
|
function Player:garbageRise(color,amount,line)--Release n-lines garbage to field
|
||||||
local _
|
local _
|
||||||
@@ -540,15 +531,15 @@ function Player:freshBlock(mode)--string mode: push/move/fresh/newBlock
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function Player:checkAIdest()
|
function Player:checkDest()
|
||||||
if not self.AI_dest then return end
|
if not self.AI_dest then return end
|
||||||
local dest=self.AI_dest
|
local dest=self.AI_dest
|
||||||
local CB=self.cur.bk
|
local CB=self.cur.bk
|
||||||
for k=1,#dest,2 do
|
for k=1,#dest,2 do
|
||||||
local r=CB[dest[k+1]-self.curY+2]
|
local r=CB[dest[k+1]-self.curY+2]
|
||||||
if not r or not r[dest[k]-self.curX+2]then
|
if not r or not r[dest[k]-self.curX+2]then
|
||||||
if self.AI_mode=='CC'then
|
if self.bot then
|
||||||
CC.updateField(self)
|
self.bot:lockWrongPlace()
|
||||||
end
|
end
|
||||||
self.AI_dest=nil
|
self.AI_dest=nil
|
||||||
return
|
return
|
||||||
@@ -837,10 +828,10 @@ function Player:hold(ifpre)
|
|||||||
SFX.play(ifpre and'prehold'or'hold')
|
SFX.play(ifpre and'prehold'or'hold')
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.AI_mode=='CC'then
|
if self.bot then
|
||||||
local next=self.nextQueue[self.AIdata.nextCount]
|
local next=self.nextQueue[ENV.nextCount]
|
||||||
if next then
|
if next then
|
||||||
CC.addNext(self.AI_bot,next.id)
|
self.bot:pushNewNext(next.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -849,23 +840,24 @@ function Player:hold(ifpre)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Player:getBlock(id,name,color)--Get a block object
|
function Player:getBlock(id,name,color)--Get a block object
|
||||||
local E=self.gameEnv
|
local ENV=self.gameEnv
|
||||||
local dir=E.face[id]
|
local dir=ENV.face[id]
|
||||||
return{
|
return{
|
||||||
id=id,
|
id=id,
|
||||||
dir=dir,
|
dir=dir,
|
||||||
bk=BLOCKS[id][dir],
|
bk=BLOCKS[id][dir],
|
||||||
rs=self.RS,
|
rs=self.RS,
|
||||||
name=name or id,
|
name=name or id,
|
||||||
color=E.bone and 17 or color or E.skin[id],
|
color=ENV.bone and 17 or color or ENV.skin[id],
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
function Player:getNext(id)--Push a block to nextQueue
|
function Player:getNext(id)--Push a block to nextQueue
|
||||||
ins(self.nextQueue,self:getBlock(id))
|
ins(self.nextQueue,self:getBlock(id))
|
||||||
end
|
end
|
||||||
function Player:popNext(ifhold)--Pop nextQueue to hand
|
function Player:popNext(ifhold)--Pop nextQueue to hand
|
||||||
|
local ENV=self.gameEnv
|
||||||
if not ifhold then
|
if not ifhold then
|
||||||
self.holdTime=min(self.holdTime+1,self.gameEnv.holdCount)
|
self.holdTime=min(self.holdTime+1,ENV.holdCount)
|
||||||
end
|
end
|
||||||
self.spinLast=false
|
self.spinLast=false
|
||||||
self.spinSeq=0
|
self.spinSeq=0
|
||||||
@@ -873,28 +865,28 @@ function Player:popNext(ifhold)--Pop nextQueue to hand
|
|||||||
|
|
||||||
self.cur=rem(self.nextQueue,1)
|
self.cur=rem(self.nextQueue,1)
|
||||||
self.newNext()
|
self.newNext()
|
||||||
|
if self.bot then
|
||||||
|
local next=self.nextQueue[ENV.nextCount]
|
||||||
|
if next then
|
||||||
|
self.bot:pushNewNext(next.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
if self.cur then
|
if self.cur then
|
||||||
self.pieceCount=self.pieceCount+1
|
self.pieceCount=self.pieceCount+1
|
||||||
if self.AI_mode=='CC'then
|
|
||||||
local next=self.nextQueue[self.AIdata.next]
|
|
||||||
if next then
|
|
||||||
CC.addNext(self.AI_bot,next.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local pressing=self.keyPressing
|
local pressing=self.keyPressing
|
||||||
|
|
||||||
--IHS
|
--IHS
|
||||||
if not ifhold and pressing[8]and self.gameEnv.ihs and self.holdTime>0 then
|
if not ifhold and pressing[8]and ENV.ihs and self.holdTime>0 then
|
||||||
self:hold(true)
|
self:hold(true)
|
||||||
pressing[8]=false
|
pressing[8]=false
|
||||||
else
|
else
|
||||||
self:resetBlock()
|
self:resetBlock()
|
||||||
end
|
end
|
||||||
|
|
||||||
self.dropDelay=self.gameEnv.drop
|
self.dropDelay=ENV.drop
|
||||||
self.lockDelay=self.gameEnv.lock
|
self.lockDelay=ENV.lock
|
||||||
self.freshTime=self.gameEnv.freshLimit
|
self.freshTime=ENV.freshLimit
|
||||||
|
|
||||||
if self.cur then
|
if self.cur then
|
||||||
if self:ifoverlap(self.cur.bk,self.curX,self.curY)then
|
if self:ifoverlap(self.cur.bk,self.curX,self.curY)then
|
||||||
@@ -1102,7 +1094,9 @@ do--Player.drop(self)--Place piece
|
|||||||
dospin=dospin+2
|
dospin=dospin+2
|
||||||
end
|
end
|
||||||
|
|
||||||
self:checkAIdest()
|
if self.bot then
|
||||||
|
self:checkDest()
|
||||||
|
end
|
||||||
self:lock()
|
self:lock()
|
||||||
|
|
||||||
--Clear list of cleared-rows
|
--Clear list of cleared-rows
|
||||||
@@ -1522,46 +1516,12 @@ do--Player.drop(self)--Place piece
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
function Player:loadAI(data)--Load AI params
|
function Player:loadAI(data)--Load AI params
|
||||||
if not CC then
|
self.bot=BOT.new(self,data)
|
||||||
data.type='9S'
|
self.bot.data=data
|
||||||
data.delta=int(data.delta*.3)
|
end
|
||||||
end
|
function Player:reloadAI()--Load AI params
|
||||||
self.AI_mode=data.type
|
assert(self.bot,"Cannot reload AI before loading one!")
|
||||||
self.AI_keys={}
|
self.bot=BOT.load(self.bot.data)
|
||||||
self.AI_delay=min(int(self.gameEnv.drop*.8),data.delta*rnd()*4)
|
|
||||||
self.AI_delay0=data.delta
|
|
||||||
self.AIdata={
|
|
||||||
type=data.type,
|
|
||||||
delay=data.delay,
|
|
||||||
delta=data.delta,
|
|
||||||
|
|
||||||
next=data.next,
|
|
||||||
hold=data.hold,
|
|
||||||
_20G=self._20G,
|
|
||||||
bag=data.bag,
|
|
||||||
node=data.node,
|
|
||||||
}
|
|
||||||
if self.AI_mode=='CC'then
|
|
||||||
self:setRS('SRS')
|
|
||||||
local opt,wei=CC.getConf()
|
|
||||||
CC.fastWeights(wei)
|
|
||||||
CC.setHold(opt,self.AIdata.hold)
|
|
||||||
CC.set20G(opt,self.AIdata._20G)
|
|
||||||
CC.setBag(opt,self.AIdata.bag=='bag')
|
|
||||||
CC.setNode(opt,self.AIdata.node)
|
|
||||||
self.AI_bot=CC.new(opt,wei)
|
|
||||||
CC.free(opt)CC.free(wei)
|
|
||||||
for i=1,self.AIdata.next do
|
|
||||||
CC.addNext(self.AI_bot,self.nextQueue[i].id)
|
|
||||||
end
|
|
||||||
if self.gameEnv.holdCount and self.gameEnv.holdCount>1 then
|
|
||||||
self:setHold(1)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self:setRS('TRS')
|
|
||||||
end
|
|
||||||
self.AI_thread=coroutine.wrap(AIFUNC[data.type])
|
|
||||||
self:AI_thread(self.AI_keys)
|
|
||||||
end
|
end
|
||||||
--------------------------</Methods>--------------------------
|
--------------------------</Methods>--------------------------
|
||||||
|
|
||||||
@@ -1690,10 +1650,8 @@ function Player:revive()
|
|||||||
self.field[_],self.visTime[_]=nil
|
self.field[_],self.visTime[_]=nil
|
||||||
end
|
end
|
||||||
self.garbageBeneath=0
|
self.garbageBeneath=0
|
||||||
if self.AI_mode=='CC'then
|
if self.bot then
|
||||||
self:destroyBot()
|
self.bot:revive()
|
||||||
TABLE.cut(self.holdQueue)
|
|
||||||
self:loadAI(self.AIdata)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self:clearAttackBuffer()
|
self:clearAttackBuffer()
|
||||||
|
|||||||
@@ -183,26 +183,8 @@ function update.alive(P,dt)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if P.type=='computer'and P.control and P.waiting==-1 then
|
if P.type=='computer'then
|
||||||
local C=P.AI_keys
|
P.bot:update(dt)
|
||||||
P.AI_delay=P.AI_delay-1
|
|
||||||
if not C[1]then
|
|
||||||
if P.AI_thread then
|
|
||||||
if not pcall(P.AI_thread)then
|
|
||||||
P:destroyBot()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
P:act_hardDrop()
|
|
||||||
end
|
|
||||||
elseif P.AI_delay<=0 then
|
|
||||||
if P.AI_mode~='CC'or C[1]>3 then
|
|
||||||
P.AI_delay=P.AI_delay0*2
|
|
||||||
else
|
|
||||||
P.AI_delay=P.AI_delay0*.5
|
|
||||||
end
|
|
||||||
P:pressKey(C[1])P:releaseKey(C[1])
|
|
||||||
rem(C,1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--Fresh visible time
|
--Fresh visible time
|
||||||
@@ -357,11 +339,6 @@ function update.alive(P,dt)
|
|||||||
|
|
||||||
if P.ghoY~=P.curY then
|
if P.ghoY~=P.curY then
|
||||||
P.dropDelay=ENV.drop
|
P.dropDelay=ENV.drop
|
||||||
elseif P.AI_mode=='CC'and P.AI_bot then
|
|
||||||
CC.updateField(P)
|
|
||||||
if not P.AIdata._20G and ENV.drop<P.AI_delay0*.5 then
|
|
||||||
CC.switch20G(P)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
P.lockDelay=P.lockDelay-1
|
P.lockDelay=P.lockDelay-1
|
||||||
@@ -369,8 +346,8 @@ function update.alive(P,dt)
|
|||||||
goto THROW_stop
|
goto THROW_stop
|
||||||
end
|
end
|
||||||
P:drop(true)
|
P:drop(true)
|
||||||
if P.AI_mode=='CC'and P.AI_bot then
|
if P.bot then
|
||||||
CC.updateField(P)
|
P.bot:unexpectedLock()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user