local DIR_TYPE = {
LEFT = 0,
RIGHT = 1,
}
local GRID_STATE = {
NONE = 0,
MARK = 1,
}
local PROP_TYPE = {
NONE = 0,
DRINKS = 1,
SKI = 2,
CHAIN = 3,
DICE_1 = 4,
DICE_2 = 5,
DICE_3 = 6,
DICE_4 = 7,
DICE_5 = 8,
DICE_6 = 9,
}
local DICE_POINT = {
[PROP_TYPE.DICE_1] = 1,
[PROP_TYPE.DICE_2] = 2,
[PROP_TYPE.DICE_3] = 3,
[PROP_TYPE.DICE_4] = 4,
[PROP_TYPE.DICE_5] = 5,
[PROP_TYPE.DICE_6] = 6,
}
local function isDice(propType)
if DICE_POINT[propType] then
return true
end
return false
end
local ENERGY_MAX = 3
local POINT_COL = 6
local POINT_ROW = 6
local DefaultClickCellIndex = nil
local COLLECT_TARGET = 30
local PROP_COUNT_LIST = {
[PROP_TYPE.DRINKS] = 5,
[PROP_TYPE.SKI] = 2,
[PROP_TYPE.CHAIN] = 1,
[PROP_TYPE.DICE_1] = 4,
[PROP_TYPE.DICE_2] = 3,
[PROP_TYPE.DICE_3] = 2,
[PROP_TYPE.DICE_4] = 1,
[PROP_TYPE.DICE_5] = 1,
[PROP_TYPE.DICE_6] = 1,
}
local LoopCount = 10000
local DebugOut = false
if DebugOut then
LoopCount = 1
end
local INVALID_NUM = 0
local MAX_ROW = 5
local MAX_COL = 5
local CELL_COUNT = MAX_ROW * MAX_COL
local TableUtil = {}
TableUtil.shuffle = function(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
local function index2Coord(index)
return math.ceil(index / MAX_COL), (index - 1) % MAX_COL + 1
end
local function coord2Index(row, col)
return (row - 1) * MAX_COL + col
end
local function cardCoord2pointCoord(row, col)
return MAX_ROW - row,col - 1
end
local function pointCoord2poinIndex(row, col)
if row % 2 == 1 then col = POINT_COL - 1 - col end
return row * POINT_COL + col
end
local function pointIndex2pointCoord(index)
local col = index % POINT_COL
local row = math.floor(index / POINT_COL)
if row % 2 == 1 then col = POINT_COL - 1 - col end
return row, col
end
local function getPointDir(point)
local y = math.floor(point / POINT_COL)
return y % 2 == 1 and DIR_TYPE.LEFT or DIR_TYPE.RIGHT
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, MAX_ROW, 1 do
for col = 1, MAX_COL, 1 do
local cellIndex = coord2Index(row, col)
local cell = value[cellIndex]
str = str .. " " .. cell
end
if row ~= MAX_ROW then
str = str .. "\r\n"
end
end
print(str)
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
local function getOneCell(cellNum, cellType)
local cell = { }
cell.mNum_ = cellNum
cell.mType_ = cellType
function cell:getDescription()
local cellNumStr = " "
if self.mNum_ ~= INVALID_NUM 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_ ~= INVALID_NUM, "Cell has been tagged.")
self.mNum_ = INVALID_NUM
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, MAX_ROW, 1 do
for j = 1, MAX_COL, 1 do
local cellIndex = (i - 1) * MAX_COL + j
local cell = board[cellIndex]
str = str .. string.format("%s ", cell:getDescription())
end
if i ~= MAX_ROW then
str = str .. "\r\n"
end
end
if ballStr ~= INVALID_NUM 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 getOneBoardConfig()
local propConfig = {}
local allProp = {}
for pType, pCount in pairs(PROP_COUNT_LIST) do
for idx = 1, pCount, 1 do
table.insert(allProp, pType)
end
end
for idx = 1, MAX_ROW * MAX_COL, 1 do
propConfig[idx] = allProp[idx] or PROP_TYPE.NONE
end
TableUtil.shuffle(propConfig)
return propConfig
end
math.randomseed(os.time())
local function simulateReadBall()
local balls = { }
for i = 1, CELL_COUNT do
table.insert(balls, i)
end
local _propConfig = getOneBoardConfig()
local board = { }
for i = 1, CELL_COUNT do
table.insert(board, getOneCell(i, _propConfig[i]))
end
local _markList = {}
local _isBingo = 0
local _totaleScore = 0
local _scoreWhen15 = 0
local _readBallCount = 0
local _collections = 0
for i = 1, MAX_ROW * MAX_COL do
_markList[i] = GRID_STATE.NONE
end
local _rolePoint = 0
local _waitPropList = {}
local _energyCollect = 0
local numToBoardIndex = { }
for cellIndex, cell in ipairs(board) do
if cell.mNum_ ~= INVALID_NUM then
numToBoardIndex[cell.mNum_] = cellIndex
end
end
TableUtil.shuffle(balls)
local function checkUseChain()
if _rolePoint >= COLLECT_TARGET then
return false
end
local isUse = false
for index, propInfo in ipairs(_waitPropList) do
if propInfo.gridType == PROP_TYPE.CHAIN then
if _rolePoint == propInfo.startP1 then
_rolePoint = propInfo.endP1
table.remove(_waitPropList, index)
isUse = true
checkUseChain()
break
elseif _rolePoint == propInfo.startP2 then
_rolePoint = propInfo.endP2
table.remove(_waitPropList, index)
isUse = true
checkUseChain()
break
end
end
end
return isUse
end
local function checkAddDrinks()
if _rolePoint >= COLLECT_TARGET then
return false
end
for index, propInfo in ipairs(_waitPropList) do
if propInfo.gridType == PROP_TYPE.DRINKS then
if _energyCollect >= ENERGY_MAX then
return false
else
_energyCollect = math.abs(_energyCollect) + 1
table.remove(_waitPropList, index)
return true
end
end
end
end
local function checkUseDice()
if _rolePoint >= COLLECT_TARGET then
return 0
end
for index, propInfo in ipairs(_waitPropList) do
local moveCount = DICE_POINT[propInfo.gridType]
if moveCount then
table.remove(_waitPropList, index)
if _energyCollect > 0 then
if _energyCollect == 3 then
moveCount = moveCount * 2
_energyCollect = 0
else
moveCount = moveCount + _energyCollect
_energyCollect = - _energyCollect
end
end
local realCount = 0
for i = 1, moveCount do
if _rolePoint < COLLECT_TARGET then
_rolePoint = _rolePoint + 1
realCount = realCount + 1
checkUseChain()
end
end
return realCount
end
end
end
local function checkUseSki()
if _rolePoint >= COLLECT_TARGET then
return false
end
local isUse = false
local moveCount = nil
local row, col = pointIndex2pointCoord(_rolePoint)
for index, propInfo in ipairs(_waitPropList) do
if propInfo.gridType == PROP_TYPE.SKI then
if row % 2 == 1 then
isUse = true
moveCount = col
table.remove(_waitPropList, index)
else
isUse = true
moveCount = POINT_COL - col - 1
table.remove(_waitPropList, index)
end
break
end
end
if moveCount then
for i = 1, moveCount do
_rolePoint = _rolePoint + 1
if checkUseChain() then
break
end
end
end
return isUse
end
local function checkUseProp()
local isUseProp = false
for index, propInfo in ipairs(_waitPropList) do
if propInfo.gridType == PROP_TYPE.DRINKS then
if checkAddDrinks() then
isUseProp = true
break
end
elseif propInfo.gridType == PROP_TYPE.CHAIN then
if checkUseChain() then
isUseProp = true
break
end
elseif propInfo.gridType == PROP_TYPE.SKI then
if checkUseSki() then
isUseProp = true
break
end
elseif isDice(propInfo.gridType) then
local moveCount = checkUseDice()
if moveCount > 0 then
isUseProp = true
break
end
end
end
if isUseProp then
checkUseProp()
end
end
local function removeOnlyGrid(gridIndex)
if _markList[gridIndex] == GRID_STATE.MARK then
return
end
_markList[gridIndex] = GRID_STATE.MARK
local propType = _propConfig[gridIndex]
if propType == PROP_TYPE.CHAIN then
local row, rol = index2Coord(gridIndex)
local pointRow, pointRol = cardCoord2pointCoord(row, rol)
local startP1, startP2 = pointCoord2poinIndex(pointRow, pointRol), pointCoord2poinIndex(pointRow, pointRol + 1)
local endP1, endP2 = pointCoord2poinIndex(pointRow + 1, pointRol), pointCoord2poinIndex(pointRow + 1, pointRol + 1)
local isOpen = false
if ((_rolePoint > startP1 and _rolePoint > startP2) or gridIndex <= 5 or isOpen) then
local emptyList = {}
for index = 1, CELL_COUNT do
if _markList[index] ~= GRID_STATE.MARK and _propConfig[index] ~= PROP_TYPE.CHAIN then
table.insert(emptyList, index)
end
end
TableUtil.shuffle(emptyList)
if emptyList[1] then
_propConfig[gridIndex] = _propConfig[emptyList[1]]
_propConfig[emptyList[1]] = PROP_TYPE.CHAIN
propType = _propConfig[gridIndex]
end
propType = _propConfig[gridIndex]
else
table.insert(_waitPropList, {gridIndex = gridIndex, gridType = propType, startP1 = startP1, startP2 = startP2, endP1 = endP1, endP2 = endP2})
end
end
if propType == PROP_TYPE.SKI then
local row, col = pointIndex2pointCoord(_rolePoint)
local needChange = false
if row % 2 == 1 then
if col < 1 then
needChange = true
end
else
if col > 4 then
needChange = true
end
end
if needChange then
local emptyList = {}
for index = 1, CELL_COUNT do
if _markList[index] ~= GRID_STATE.MARK and _propConfig[index] ~= PROP_TYPE.NONE then
table.insert(emptyList, index)
end
end
TableUtil.shuffle(emptyList)
if emptyList[1] then
_propConfig[gridIndex] = _propConfig[emptyList[1]]
_propConfig[emptyList[1]] = PROP_TYPE.SKI
propType = _propConfig[gridIndex]
end
propType = _propConfig[gridIndex]
else
table.insert(_waitPropList, {gridIndex = gridIndex, gridType = propType})
end
end
if propType == PROP_TYPE.DRINKS then
table.insert(_waitPropList, {gridIndex = gridIndex, gridType = propType})
end
if isDice(propType) then
table.insert(_waitPropList, {gridIndex = gridIndex, gridType = propType})
end
checkUseProp()
if DebugOut then
dump_board(_markList, gridIndex .. " " .. propType .. " " .. _rolePoint)
end
_collections = _rolePoint
end
local function updateLogic(gridIndex)
removeOnlyGrid(gridIndex)
end
local function checkBingo()
if _totaleScore >= COLLECT_TARGET then
_isBingo = 1
return true
end
return false
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
_totaleScore = _collections
if _totaleScore > COLLECT_TARGET then
_totaleScore = COLLECT_TARGET
end
if DebugOut then
PrintCurrentState(board, _totaleScore, ballNum)
end
if _readBallCount <= 15 then
_scoreWhen15 = _totaleScore
end
if checkBingo() then
break
end
end
return _readBallCount, _scoreWhen15
end
local bingoNeedReadBall = { }
for i = 1, CELL_COUNT 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, CELL_COUNT do
bingoTotal[i] = bingoNeedReadBall[i] + bingoTotal[i - 1]
end
print("标记格子数量发生bingo:", table.concat(bingoNeedReadBall, ", "))
print("标记累计发生bingo数量:", table.concat(bingoTotal, ", "))
print("第15次读球分::", bingoTotal[15])
print("第15次读球累计bingo数量:", allTotalScoreWhen15 / LoopCount)