local propEnum = {
None = 0,
EggN1 = 1,
EggN2 = 2,
EggN3 = 3,
EggS1 = 4,
EggS2 = 5,
EggS3 = 6,
Branch = 7,
Count = 8,
}
local PropEnum = propEnum
local numberOfProp = {
[propEnum.EggN1] = 12,
[propEnum.EggS1] = 8,
[propEnum.Branch] = 5,
}
local ScoreGetBingo = 8
local LoopCount = 10000
local DebugOut = false
if DebugOut then
LoopCount = 1
end
local InvalidNum = 0
local RowCount = 5
local ColumnCount = 5
local CellCount = RowCount * ColumnCount
math.randomseed(os.time())
local function indexToRowColumn(index)
return math.floor((index - 1) / ColumnCount) + 1, (index - 1) % ColumnCount + 1
end
local function rowColumnToIndex(row, column)
if row <= 0 or column <= 0 or row > RowCount or column > ColumnCount then
return nil
end
return (row - 1) * ColumnCount + column
end
local function getAroundIndexs(index)
local indexs = { }
local row, column = indexToRowColumn(index)
for tempRow = row - 1, row + 1, 1 do
for tempColumn = column - 1, column + 1, 1 do
if tempRow ~= row or tempColumn ~= column then
if tempRow >= 1 and tempRow <= RowCount and tempColumn >= 1 and tempColumn <= ColumnCount then
table.insert(indexs, rowColumnToIndex(tempRow, tempColumn))
end
end
end
end
return indexs
end
local function getCrossIndexs(index)
local indexs = { }
local row, column = indexToRowColumn(index)
local tempCoordinates = {
{ row - 1, column },
{ row + 1, column },
{ row, column + 1 },
{ row, column - 1 },
}
for _, config in pairs(tempCoordinates) do
local tempRow = config[1]
local tempColumn = config[2]
if tempRow >= 1 and tempRow <= RowCount and tempColumn >= 1 and tempColumn <= ColumnCount then
table.insert(indexs, rowColumnToIndex(tempRow, tempColumn))
end
end
return indexs
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.contain(t, v)
for _, value in pairs(t) do
if value == v then
return true
end
end
return false
end
local function dump(list, string)
local str = "" .. string
for k, v in ipairs(list) do
str = str .. v .. ", "
end
print(str)
end
local PropToEmoji = {
[PropEnum.EggN1] = "��",
[PropEnum.EggS1] = "��",
[PropEnum.Branch] = "��",
[PropEnum.EggN2] = "��",
[PropEnum.EggS2] = "��",
[PropEnum.EggN3] = "��",
[PropEnum.EggS3] = "��",
}
if DebugOut then
print(string.format("Up route: %s -> %s -> %s", PropToEmoji[PropEnum.EggN1], PropToEmoji[PropEnum.EggN2], PropToEmoji[PropEnum.EggN3]))
print(string.format("Up route: %s -> %s -> %s", PropToEmoji[PropEnum.EggS1], PropToEmoji[PropEnum.EggS2], PropToEmoji[PropEnum.EggS3]))
end
local function getOneCell(number)
local cell = { }
cell.cellNum = number
cell.tag = propEnum.None
function cell:resetTag()
self.tag = propEnum.None
end
function cell:setTag(tag)
self.tag = tag
end
function cell:getDescription()
local cellNumStr = "❎"
if self.cellNum ~= InvalidNum then
cellNumStr = tostring(self.cellNum)
if string.len(cellNumStr) == 1 then
cellNumStr = cellNumStr .. " "
end
end
local cellTagStr = " "
if self.tag ~= propEnum.None then
cellTagStr = PropToEmoji[self.tag]
end
return string.format("{%s, %s}", cellNumStr, cellTagStr)
end
function cell:beenTagged()
if self.cellNum == InvalidNum then
return false, 0, 0
end
self.cellNum = InvalidNum
return true, self.tag
end
return cell
end
local function addTagToBoard(board)
local addPropQueue = { }
for propId = PropEnum.None + 1, PropEnum.Count - 1, 1 do
local tagCount = numberOfProp[propId] or 0
while tagCount > 0 do
table.insert(addPropQueue, propId)
tagCount = tagCount - 1
end
end
shuffleTable(addPropQueue)
local candidateCellIndexs = { }
for cellIndex = 1, CellCount, 1 do
table.insert(candidateCellIndexs, cellIndex)
end
shuffleTable(candidateCellIndexs)
while #addPropQueue > 0 and #candidateCellIndexs > 0 do
local propId = table.remove(addPropQueue, 1)
local tagCellIndex = table.remove(candidateCellIndexs, 1)
board[tagCellIndex]:setTag(propId)
end
end
local function PrintCurrentState(board, currentScore, ballStr, extraMsg, ignore)
local str = ""
for i = 1, RowCount, 1 do
for j = 1, ColumnCount, 1 do
local cellIndex = (i - 1) * ColumnCount + j
local cell = board[cellIndex]
str = str .. string.format("%s ", cell:getDescription())
end
if i ~= RowCount then
str = str .. "\r\n"
end
end
if ballStr and ballStr ~= "" then
print(ballStr)
end
print(str)
if extraMsg and extraMsg ~= "" then
print(extraMsg)
end
if currentScore then
print("Current score: ", currentScore)
end
if not ignore then
print()
end
end
local function simulateReadBall()
local balls = { }
for i = 1, CellCount do
table.insert(balls, i)
end
local board = { }
for i = 1, CellCount do
table.insert(board, getOneCell(i))
end
shuffleTable(board)
addTagToBoard(board)
local numToBoardIndex = { }
for cellIndex, cell in ipairs(board) do
if cell.cellNum ~= InvalidNum then
numToBoardIndex[cell.cellNum] = cellIndex
end
end
shuffleTable(balls)
local totalScore = 0
local theScoreWhen15 = 0
local readBallCount = 0
if DebugOut then
PrintCurrentState(board, nil, nil, nil, false)
end
local propPositions = { }
while #balls > 0 do
readBallCount = readBallCount + 1
local ballNum = balls[1]
table.remove(balls, 1)
local readIndex = numToBoardIndex[ballNum]
assert(readIndex ~= nil, string.format("Cant find ball num index: %d", ballNum))
assert(board[readIndex].cellNum == ballNum or board[readIndex].cellNum == InvalidNum, "NumToBoardIndex Map Error!")
local success, propType = board[readIndex]:beenTagged()
if propType ~= PropEnum.None then
if propType == PropEnum.Branch then
table.insert(propPositions, { readIndex, propType, 2, { } })
else
table.insert(propPositions, { readIndex, propType, 1 })
end
end
local effectInfos = {
{ "click", readIndex }
}
while #effectInfos > 0 do
while #effectInfos > 0 do
local nextInfos = { }
for _, effectInfo in ipairs(effectInfos) do
local crossIndexs
if effectInfo[1] == "click" then
crossIndexs = getCrossIndexs(effectInfo[2])
else
crossIndexs = getAroundIndexs(effectInfo[2])
end
for _, tempCellIndex in pairs(crossIndexs) do
for i = #propPositions, 1, -1 do
if propPositions[i][1] == tempCellIndex then
if propPositions[i][2] == PropEnum.EggN1 then
propPositions[i][2] = PropEnum.EggN2
board[tempCellIndex]:setTag(propPositions[i][2])
elseif propPositions[i][2] == PropEnum.EggN2 then
totalScore = totalScore + 1
table.remove(propPositions, i)
board[tempCellIndex]:setTag(PropEnum.EggN3)
elseif propPositions[i][2] == PropEnum.EggS1 then
propPositions[i][2] = PropEnum.EggS2
board[tempCellIndex]:setTag(propPositions[i][2])
elseif propPositions[i][2] == PropEnum.EggS2 then
totalScore = totalScore + 1
table.remove(propPositions, i)
board[tempCellIndex]:setTag(PropEnum.EggS3)
table.insert(nextInfos, { "superB", tempCellIndex })
end
end
end
end
end
effectInfos = nextInfos
end
local eggIndexs = { }
local treeBranchIndexs = { }
for i, propPosition in ipairs(propPositions) do
if propPosition[3] > 0 then
local propId = propPosition[2]
if (propId >= PropEnum.EggN1 and propId <= PropEnum.EggN2)
or (propId >= PropEnum.EggS1 and propId <= PropEnum.EggS2) then
table.insert(eggIndexs, i)
elseif propId == PropEnum.Branch then
table.insert(treeBranchIndexs, i)
end
end
end
while #treeBranchIndexs > 0 and #eggIndexs > 0 do
local nextInfos = { }
table.sort(eggIndexs, function(tempEggIndex1, tempEggIndex2)
if propPositions[tempEggIndex1][2] == propPositions[tempEggIndex2][2] then
return tempEggIndex1 < tempEggIndex2
else
return propPositions[tempEggIndex1][2] > propPositions[tempEggIndex2][2]
end
end)
local bracnhPropIndex = table.remove(treeBranchIndexs, 1)
local branchPropInfo = propPositions[bracnhPropIndex]
if branchPropInfo[3] > 0 and #eggIndexs > 0 then
for _, eggPropIndex in ipairs(eggIndexs) do
local eggPropInfo = propPositions[eggPropIndex]
local eggCellIndex = eggPropInfo[1]
if eggPropInfo[3] > 0 and not branchPropInfo[4][eggCellIndex] then
if eggPropInfo[2] == PropEnum.EggN1 then
eggPropInfo[2] = PropEnum.EggN2
board[eggCellIndex]:setTag(eggPropInfo[2])
elseif eggPropInfo[2] == PropEnum.EggN2 then
totalScore = totalScore + 1
eggPropInfo[3] = eggPropInfo[3] - 1
board[eggCellIndex]:setTag(PropEnum.EggN3)
elseif eggPropInfo[2] == PropEnum.EggS1 then
eggPropInfo[2] = PropEnum.EggS2
board[eggCellIndex]:setTag(eggPropInfo[2])
elseif eggPropInfo[2] == PropEnum.EggS2 then
totalScore = totalScore + 1
eggPropInfo[3] = eggPropInfo[3] - 1
board[eggCellIndex]:setTag(PropEnum.EggS3)
table.insert(nextInfos, { "superB", eggCellIndex })
end
branchPropInfo[3] = branchPropInfo[3] - 1
branchPropInfo[4][eggCellIndex] = true
if #nextInfos > 0 or branchPropInfo[3] == 0 then
break
end
end
end
end
effectInfos = nextInfos
if #nextInfos > 0 then
break
end
end
for i = #propPositions, 1, -1 do
if propPositions[i][3] == 0 then
table.remove(propPositions, i)
end
end
end
local ballStr = string.format("读球: %d --- %d --- %d", readIndex, ballNum, readBallCount)
if DebugOut then
PrintCurrentState(board, totalScore, ballStr)
end
if readBallCount <= 15 then
theScoreWhen15 = totalScore
end
if totalScore >= ScoreGetBingo then
break
end
end
if theScoreWhen15 > ScoreGetBingo then
theScoreWhen15 = ScoreGetBingo
end
return readBallCount, theScoreWhen15, totalScore >= ScoreGetBingo
end
local bingoNeedReadBall = { }
for i = 1, CellCount do
bingoNeedReadBall[i] = 0
end
local allTotalScoreWhen15 = 0
for i = 1, LoopCount do
local ballCountCompleteBingo, snailNodePositionWhen15, bingo = simulateReadBall()
if bingo then
bingoNeedReadBall[ballCountCompleteBingo] = bingoNeedReadBall[ballCountCompleteBingo] + 1
end
allTotalScoreWhen15 = allTotalScoreWhen15 + snailNodePositionWhen15
end
local bingoTotal = { }
bingoTotal[1] = 0
for i = 2, CellCount do
bingoTotal[i] = bingoNeedReadBall[i] + bingoTotal[i - 1]
end
local strH = ""
local str1 = ""
local str2 = ""
for i = 1, #bingoNeedReadBall, 1 do
local l1 = string.len(tostring(bingoNeedReadBall[i]))
local l2 = string.len(tostring(bingoTotal[i]))
local l = l1 > l2 and l1 or l2
if l < string.len(tostring(i)) then
l = string.len(tostring(i))
end
strH = strH .. string.format(string.format("%%%dd", l), i)
str1 = str1 .. string.format(string.format("%%%dd", l), bingoNeedReadBall[i])
str2 = str2 .. string.format(string.format("%%%dd", l), bingoTotal[i])
if i ~= #bingoNeedReadBall then
strH = strH .. ", "
str1 = str1 .. ", "
str2 = str2 .. ", "
end
end
print(" ", strH)
print("daub bingo count: ", str1)
print("cumulative bingo: ", str2)
print("aver score at 15: ", allTotalScoreWhen15 / LoopCount)
if DebugOut then
print(string.format("Up route: %s -> %s -> %s", PropToEmoji[PropEnum.EggN1], PropToEmoji[PropEnum.EggN2], PropToEmoji[PropEnum.EggN3]))
print(string.format("Up route: %s -> %s -> %s", PropToEmoji[PropEnum.EggS1], PropToEmoji[PropEnum.EggS2], PropToEmoji[PropEnum.EggS3]))
end