local GRID_STATE = {
NONE = 0,
MARK = 1,
}
local PROP_TYPE = {
NONE = 0,
GHOST_1 = 1,
GHOST_2 = 2,
CANDLE = 3,
PUMPKIN_LAMP = 4,
CANDY_LAMP = 5,
}
local PROP_BIG_TYPE = {
GHOST = 1,
CANDLE = 2,
PUMPKIN_LAMP_1 = 3,
PUMPKIN_LAMP_2 = 4,
CANDY_LAMP = 5,
}
local DefaultClickCellIndex = nil
local COLLECT_TARGET = 8
local PROP_COUNT_LIST = {
[PROP_TYPE.GHOST_1] = 4,
[PROP_TYPE.GHOST_2] = 5,
[PROP_TYPE.CANDLE] = 5,
[PROP_TYPE.PUMPKIN_LAMP] = 3,
[PROP_TYPE.CANDY_LAMP] = 2,
}
local PUMPKIN_LAMP_RANG = {7, 8, 9, 12, 14, 17, 18, 19}
local LoopCount = 10000
local DebugOut = false
if DebugOut then
LoopCount = 1
end
local InvalidNum = 0
local MaxRow = 5
local MaxCol = 5
local CellCount = MaxRow * MaxCol
local function grid2Pos(grid)
return math.ceil(grid / MaxCol), (grid - 1) % MaxRow + 1
end
local function pos2Grid(row, col)
return (row - 1) * MaxCol + col
end
local function dump_value_(v)
if type(v) == "string" then
v = "\"" .. v .. "\""
end
return tostring(v)
end
local function dump(value, description, nesting)
if type(nesting) ~= "number" then nesting = 3 end
local lookupTable = {}
local result = {}
local function dump_(value, description, indent, nest, keylen)
description = description or "<var>"
local spc = ""
if type(keylen) == "number" then
spc = string.rep(" ", keylen - string.len(dump_value_(description)))
end
if type(value) ~= "table" then
result[#result +1 ] = string.format("%s%s%s = %s", indent, dump_value_(description), spc, dump_value_(value))
elseif lookupTable[tostring(value)] then
result[#result +1 ] = string.format("%s%s%s = *REF*", indent, dump_value_(description), spc)
else
lookupTable[tostring(value)] = true
if nest > nesting then
result[#result +1 ] = string.format("%s%s = *MAX NESTING*", indent, dump_value_(description))
else
result[#result +1 ] = string.format("%s%s = {", indent, dump_value_(description))
local indent2 = indent.." "
local keys = {}
local keylen = 0
local values = {}
for k, v in pairs(value) do
keys[#keys + 1] = k
local vk = dump_value_(k)
local vkl = string.len(vk)
if vkl > keylen then keylen = vkl end
values[k] = v
end
table.sort(keys, function(a, b)
if type(a) == "number" and type(b) == "number" then
return a < b
else
return tostring(a) < tostring(b)
end
end)
for i, k in ipairs(keys) do
dump_(values[k], k, indent2, nest + 1, keylen)
end
result[#result +1] = string.format("%s}", indent)
end
end
end
dump_(value, description, "- ", 1)
for i, line in ipairs(result) do
print(line)
end
end
local function dump_board(value, description)
print(description or "")
local str = "\r\n"
for row = 1, MaxRow, 1 do
for col = 1, MaxCol, 1 do
local cellIndex = pos2Grid(row, col)
local cell = value[cellIndex]
str = str .. " " .. cell
end
if row ~= MaxRow then
str = str .. "\r\n"
end
end
print(str)
end
local function shuffleTable(t, startIndex, endIndex)
if startIndex == nil then
startIndex = 1
end
if endIndex == nil then
endIndex = #t
end
for i = startIndex, endIndex do
local randomIndex = math.random(startIndex, endIndex)
local tempValue = t[i]
t[i] = t[randomIndex]
t[randomIndex] = tempValue
end
end
function table.nums(t)
local count = 0
for k, v in pairs(t) do
count = count + 1
end
return count
end
function table.clone(t, nometa)
local u = {}
for i, v in pairs(t) do
if type(v) == "table" then
u[i] = table.clone(v)
else
u[i] = v
end
end
return u
end
function table.indexof(array, value, begin)
for i = begin or 1, #array do
if array[i] == value then return i end
end
return false
end
function table.removebyvalue(array, value, removeall)
local c, i, max = 0, 1, #array
while i <= max do
if array[i] == value then
table.remove(array, i)
c = c + 1
i = i - 1
max = max - 1
if not removeall then break end
end
i = i + 1
end
return c
end
local function getOneCell(cellNum, cellType)
local cell = { }
cell.mNum_ = cellNum
cell.mType_ = cellType
function cell:getDescription()
local cellNumStr = " "
if self.mNum_ ~= InvalidNum then
cellNumStr = tostring(self.mNum_)
if string.len(cellNumStr) == 1 then
cellNumStr = " " .. cellNumStr
end
end
return string.format("{%s, %s}", cellNumStr, self.mType_)
end
function cell:beenTagged()
assert(self.mNum_ ~= InvalidNum, "Cell has been tagged.")
self.mNum_ = InvalidNum
return self.mType_
end
function cell:setType(type)
self.mType_ = type
end
function cell:getType()
return self.mType_
end
return cell
end
local function PrintCurrentState(board, allScore, ballStr, getPropMsg)
local str = "\r\n"
for i = 1, MaxRow, 1 do
for j = 1, MaxCol, 1 do
local cellIndex = (i - 1) * MaxCol + j
local cell = board[cellIndex]
str = str .. string.format("%s ", cell:getDescription())
end
if i ~= MaxRow then
str = str .. "\r\n"
end
end
if ballStr ~= InvalidNum then
print("Read ball: ", ballStr)
end
print(str)
if getPropMsg and getPropMsg ~= "" then
print(getPropMsg)
end
if allScore then
print("Current total score: ", allScore)
end
print()
end
local function getGridRange(gridIndex)
local row, col = grid2Pos(gridIndex)
local rangList = {}
for i = -1, 1, 1 do
for j = -1, 1, 1 do
if not (i == 0 and j == 0) then
local index = pos2Grid(row + i, col + j)
rangList[index] = index
end
end
end
return rangList
end
local function getOneBoardConfig()
local boardConfig = {}
local pumpkinRange = table.clone(PUMPKIN_LAMP_RANG)
shuffleTable(pumpkinRange)
local pumpkinList = {}
for i = 1, PROP_COUNT_LIST[PROP_TYPE.PUMPKIN_LAMP] do
for index, pumpkinGrid in ipairs(pumpkinRange) do
local pRow, pCol = grid2Pos(pumpkinGrid)
local isAdjoin = false
for _, selectGrid in ipairs(pumpkinList) do
local sRow, sCol = grid2Pos(selectGrid)
local oRow, oCol = math.abs(sRow - pRow), math.abs(pCol - sCol)
if not (oRow == 1 and oCol == 1) and oRow <= 1 and oCol <= 1 then
isAdjoin = true
break
end
end
if not isAdjoin then
table.insert(pumpkinList, pumpkinGrid)
boardConfig[pumpkinGrid] = PROP_TYPE.PUMPKIN_LAMP
table.remove(pumpkinRange, index)
break
end
end
end
local emptyList = {}
for i = 1, MaxRow * MaxCol do
if not boardConfig[i] then
table.insert(emptyList, i)
boardConfig[i] = PROP_TYPE.NONE
end
end
shuffleTable(emptyList)
local allPropList = {}
for propType, propCount in pairs(PROP_COUNT_LIST) do
if propType ~= PROP_TYPE.PUMPKIN_LAMP then
for i = 1, propCount do
table.insert(allPropList, propType)
end
end
end
for index, propType in ipairs(allPropList) do
boardConfig[emptyList[index]] = propType
end
return boardConfig
end
math.randomseed(os.time())
local function simulateReadBall()
local balls = { }
for i = 1, CellCount do
table.insert(balls, i)
end
local boardConfig = getOneBoardConfig()
local board = { }
for i = 1, CellCount do
table.insert(board, getOneCell(i, boardConfig[i]))
end
local _isBingo = 0
local _totaleScore = 0
local _scoreWhen15 = 0
local _readBallCount = 0
local _markList = {}
for i = 1, MaxRow * MaxCol do
_markList[i] = GRID_STATE.NONE
end
local _waitPropList = {
}
for _, bigType in pairs(PROP_BIG_TYPE) do
_waitPropList[bigType] = {}
end
local numToBoardIndex = { }
for cellIndex, cell in ipairs(board) do
if cell.mNum_ ~= InvalidNum then
numToBoardIndex[cell.mNum_] = cellIndex
end
end
shuffleTable(balls)
local function checkBingo()
if _totaleScore >= COLLECT_TARGET then
_isBingo = 1
return true
end
return false
end
local useProp
local function updateLogic(gridIndex)
_markList[gridIndex] = GRID_STATE.MARK
local gridType = boardConfig[gridIndex]
if gridType == PROP_TYPE.CANDY_LAMP then
if #_waitPropList[PROP_BIG_TYPE.CANDY_LAMP] > 0 then
boardConfig[gridIndex] = PROP_TYPE.NONE
gridType = PROP_TYPE.NONE
end
end
if gridType == PROP_TYPE.GHOST_1 or gridType == PROP_TYPE.GHOST_2 then
table.insert(_waitPropList[PROP_BIG_TYPE.GHOST], gridIndex)
elseif gridType == PROP_TYPE.CANDLE then
table.insert(_waitPropList[PROP_BIG_TYPE.CANDLE], gridIndex)
elseif gridType == PROP_TYPE.PUMPKIN_LAMP then
table.insert(_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_1], gridIndex)
elseif gridType == PROP_TYPE.CANDY_LAMP then
table.insert(_waitPropList[PROP_BIG_TYPE.CANDY_LAMP], gridIndex)
end
useProp()
if DebugOut then
dump_board(_markList, gridIndex .. " " .. gridType .. " ")
end
end
local function useOnePumpkin(gridIndex)
local ghostList = {}
local rangList = getGridRange(gridIndex)
for _, ghostIndex in ipairs(_waitPropList[PROP_BIG_TYPE.GHOST]) do
if rangList[ghostIndex] then
table.insert(ghostList, ghostIndex)
if boardConfig[ghostIndex] == PROP_TYPE.GHOST_1 then
_totaleScore = _totaleScore + 1
elseif boardConfig[ghostIndex] == PROP_TYPE.GHOST_2 then
_totaleScore = _totaleScore + 2
end
end
end
if #ghostList > 0 then
for _, index in ipairs(ghostList) do
table.removebyvalue(_waitPropList[PROP_BIG_TYPE.GHOST], index)
end
end
return ghostList
end
function useProp()
if checkBingo() then
return
end
if #_waitPropList[PROP_BIG_TYPE.CANDY_LAMP] > 0 and #_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_2] > 0 then
table.remove(_waitPropList[PROP_BIG_TYPE.CANDY_LAMP], 1)
for _, gridIndex in ipairs(_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_2]) do
table.insert(_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_1], gridIndex)
end
_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_2] = {}
end
if #_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_1] > 0 then
local isUsePimpkin = false
for _, gridIndex in ipairs(_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_1]) do
local ghostList = useOnePumpkin(gridIndex)
if #ghostList > 0 then
table.removebyvalue(_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_1], gridIndex)
table.insert(_waitPropList[PROP_BIG_TYPE.PUMPKIN_LAMP_2], gridIndex)
isUsePimpkin = true
end
end
if isUsePimpkin then
useProp()
return
end
end
if #_waitPropList[PROP_BIG_TYPE.CANDLE] > 0 then
local costList = {}
for _, index in ipairs(_waitPropList[PROP_BIG_TYPE.CANDLE]) do
if _waitPropList[PROP_BIG_TYPE.GHOST][1] then
local ghostIndex = table.remove(_waitPropList[PROP_BIG_TYPE.GHOST], 1)
if boardConfig[ghostIndex] == PROP_TYPE.GHOST_1 then
_totaleScore = _totaleScore + 1
elseif boardConfig[ghostIndex] == PROP_TYPE.GHOST_2 then
_totaleScore = _totaleScore + 2
end
table.insert(costList, index)
else
break
end
end
for _, index in ipairs(costList) do
table.removebyvalue(_waitPropList[PROP_BIG_TYPE.CANDLE], index)
end
end
end
while #balls > 0 do
_readBallCount = _readBallCount + 1
local ballNum = balls[1]
table.remove(balls, 1)
local ballIndex = numToBoardIndex[ballNum]
assert(ballIndex ~= nil, string.format("Cant find ball num index: %d", ballNum))
assert(board[ballIndex].mNum_ == ballNum, "NumToBoardIndex Map Error!")
board[ballIndex]:beenTagged()
if ballIndex ~= DefaultClickCellIndex then
updateLogic(ballIndex)
end
if DebugOut then
PrintCurrentState(board, _totaleScore, ballNum)
end
if _totaleScore > COLLECT_TARGET then
_totaleScore = COLLECT_TARGET
end
if _readBallCount <= 15 then
_scoreWhen15 = _totaleScore
end
if checkBingo() then
break
end
end
return _readBallCount, _scoreWhen15
end
local bingoNeedReadBall = { }
for i = 1, CellCount do
bingoNeedReadBall[i] = 0
end
local allTotalScoreWhen15 = 0
for i = 1, LoopCount do
local ballCountCompleteBingo, snailNodePositionWhen15 = simulateReadBall()
bingoNeedReadBall[ballCountCompleteBingo] = bingoNeedReadBall[ballCountCompleteBingo] + 1
allTotalScoreWhen15 = allTotalScoreWhen15 + snailNodePositionWhen15
end
local bingoTotal = { }
bingoTotal[1] = 0
for i = 2, CellCount do
bingoTotal[i] = bingoNeedReadBall[i] + bingoTotal[i - 1]
end
print("标记格子数量发生bingo:", table.concat(bingoNeedReadBall, ", "))
print("标记累计发生bingo数量:", table.concat(bingoTotal, ", "))
print("第15次读球, 分:", allTotalScoreWhen15 / LoopCount)