local WINDOWS = package.config:sub(1,1) == "\\"; local data = {}; local score = 0; local COLOURS = { "A", "B", "C", "D", "E", "F" }; local TYPES = { "REGULAR", "SPECIAL1", "SPECIAL2" }; local deepcopy, split, initCell, init, tick, move, FindLinesAndDeleteIt, ShiftDownOldCellsAndCreateNew, mix, dump, CheckIfThereAreNotPossibleTurns; math.randomseed(1000); -- todo:delete, для отладки -- Инициализирует таблицу "data" случайными значениями function init() for x = 0, 9 do data[x] = { }; for y = 0, 9 do data[x][y] = initCell(); end end end -- Непонятно что это function tick() end function move(from, to) local oldData = deepcopy(data); -- Меняет ячейки местами в таблице "data" local aFrom = deepcopy(data[from.x][from.y]); data[from.x][from.y] = deepcopy(data[to.x][to.y]); data[to.x][to.y] = aFrom; tick(); -- ход имеет смысл? if (FindLinesAndDeleteIt(true) == 0) then -- некорректный ход data = oldData; return; end -- Находит и удаляет линии из 3+ ячеек с одинаковым цветом FindLinesAndDeleteIt(); -- Смещает оставшиеся ячейки вниз, добавляет новые сверху, уничтожает новые линии из 3+ ячеек с одинаковым цветом while (ShiftDownOldCellsAndCreateNew()) do FindLinesAndDeleteIt(); end -- Если нет возможных комбинаций, перетасовываем ячейки так, чтобы счет игрока не увеличился if (not AreTherePossibleTurns()) then mix(); end end function mix() -- Меняет случайные ячейки местами по одному, пока не появятся возможные ходы, причем так, чтобы не начислить игроку очков while (not AreTherePossibleTurns()) do local oldData = deepcopy(data); local rnd1x, rnd1y, rnd2x, rnd2y = math.random(0, 9), math.random(0, 9), math.random(0, 9), math.random(0, 9); local aFrom = deepcopy(data[rnd1x][rnd1y]); data[rnd1x][rnd1y] = deepcopy(data[rnd2x][rnd2y]); data[rnd2x][rnd2y] = aFrom; if (FindLinesAndDeleteIt(true) > 0) then data = oldData; else tick(); end end end function dump() -- Обновляем экран if (WINDOWS) then os.execute("cls"); else os.execute("clear"); end io.write(" 0 1 2 3 4 5 6 7 8 9\n - - - - - - - - - -\n"); for x = 0, 9 do io.write(tostring(x).." | "); local xarray = data[x]; for y = 0, 9 do local celldata = xarray[y]; if (celldata ~= nil) then io.write(celldata.colour.." "); else io.write(" "); end end io.write("\n"); end io.write("\nScore: "..tostring(score).."\n") io.write("\n\n>>"); end function deepcopy(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then return object elseif lookup_table[object] then return lookup_table[object] end local new_table = {} lookup_table[object] = new_table for index, value in pairs(object) do new_table[_copy(index)] = _copy(value) end return setmetatable(new_table, getmetatable(object)); end return _copy(object); end function split(inputstr, sep) sep = sep or "%s"; local t = { }; for field, s in string.gmatch(inputstr, "([^"..sep.."]*)("..sep.."?)") do table.insert(t, field); if (s == "") then return t; end end end -- Возвращает новую ячейку function initCell() return { ["colour"] = COLOURS[math.random(6)], ["type"] = TYPES[1] }; end -- Находит и удаляет линии из 3+ ячеек с одинаковым цветом -- Возвращает количество удаленных ячеек -- Если dryRun поставить в true, то актуальные данные не изменяеются function FindLinesAndDeleteIt(dryRun) local coordsToDelete = { }; for x = 0, 9 do for y = 0, 9 do local colour = data[x][y].colour; local counter = 1; local tarray = { }; for cx = 1, 9 do local tempX = x + cx; if (tempX < 10) then if (data[tempX][y].colour == colour) then counter = counter + 1; if (counter >= 3) then if (tarray ~= nil) then table.insert(coordsToDelete, tarray[1]); table.insert(coordsToDelete, tarray[2]); tarray = nil; end table.insert(coordsToDelete, {["x"] = tempX, ["y"] = y}); else table.insert(tarray, {["x"] = tempX, ["y"] = y}) end else break; end else break; end end counter = 1; tarray = { }; for cx = 1, 9 do local tempX = x - cx; if (tempX >= 0) then if (data[tempX][y].colour == colour) then counter = counter + 1; if (counter >= 3) then if (tarray ~= nil) then table.insert(coordsToDelete, tarray[1]); table.insert(coordsToDelete, tarray[2]); tarray = nil; end table.insert(coordsToDelete, {["x"] = tempX, ["y"] = y}); else table.insert(tarray, {["x"] = tempX, ["y"] = y}) end else break; end else break; end end counter = 1; tarray = { }; for cy = 1, 9 do local tempY = y + cy; if (tempY < 10) then if (data[x][tempY].colour == colour) then counter = counter + 1; if (counter >= 3) then if (tarray ~= nil) then table.insert(coordsToDelete, tarray[1]); table.insert(coordsToDelete, tarray[2]); tarray = nil; end table.insert(coordsToDelete, {["x"] = x, ["y"] = tempY}); else table.insert(tarray, {["x"] = x, ["y"] = tempY}) end else break; end else break; end end counter = 1; tarray = { }; for cy = 1, 9 do local tempY = y - cy; if (tempY >= 0) then if (data[x][tempY].colour == colour) then counter = counter + 1; if (counter >= 3) then if (tarray ~= nil) then table.insert(coordsToDelete, tarray[1]); table.insert(coordsToDelete, tarray[2]); tarray = nil; end table.insert(coordsToDelete, {["x"] = x, ["y"] = tempY}); else table.insert(tarray, {["x"] = x, ["y"] = tempY}) end else break; end else break; end end end end if (not dryRun) then for _, xyPair in pairs(coordsToDelete) do data[xyPair.x][xyPair.y] = nil; tick(); end end return #coordsToDelete; end -- Смещает оставшиеся ячейки вниз, добавляет новые сверху -- Возвращает true, если в процессе создаются новые ячейки function ShiftDownOldCellsAndCreateNew() for y = 0, 9 do for x = 9, 0, -1 do if (data[x][y] == nil) then for x2 = x-1, 0, -1 do if (data[x2][y] ~= nil) then data[x][y] = deepcopy(data[x2][y]); data[x2][y] = nil; tick(); break; end end end end end local haveNewCells = false; for x = 0, 9 do for y = 0, 9 do if (data[x][y] == nil) then data[x][y] = initCell(); tick(); score = score + 1; haveNewCells = true; end end end return haveNewCells; end -- Возвращает true, если есть доступные ходы function AreTherePossibleTurns() local oldData = deepcopy(data); local retValue = false; for x = 0, 9 do for y = 0, 9 do if (x - 1 >= 0) then local aFrom = deepcopy(data[x][y]); data[x][y] = deepcopy(data[x-1][y]); data[x-1][y] = aFrom; if (FindLinesAndDeleteIt(true) > 0) then retValue = true; end; end if (x + 1 < 10) then local aFrom = deepcopy(data[x][y]); data[x][y] = deepcopy(data[x+1][y]); data[x+1][y] = aFrom; if (FindLinesAndDeleteIt(true) > 0) then retValue = true; end; end if (y - 1 >= 0) then local aFrom = deepcopy(data[x][y]); data[x][y] = deepcopy(data[x][y-1]); data[x][y-1] = aFrom; if (FindLinesAndDeleteIt(true) > 0) then retValue = true; end; end if (y + 1 < 10) then local aFrom = deepcopy(data[x][y]); data[x][y] = deepcopy(data[x][y+1]); data[x][y+1] = aFrom; if (FindLinesAndDeleteIt(true) > 0) then retValue = true; end; end end end data = oldData; return retValue; end init(); move({ ["x"] = 0, ["y"] = 0 }, { ["x"] = 0, ["y"] = 0 }); dump(); local input = io.read("*line"); -- Тут нужно добавить всяких проверок на корректность данных while (input ~= "q") do local inputValues = split(input); if (inputValues[1] == "d") then init(); move({ ["x"] = 0, ["y"] = 0 }, { ["x"] = 0, ["y"] = 0 }); dump(); elseif (inputValues[1] == "m") then local from = { ["x"] = tonumber(inputValues[2]), ["y"] = tonumber(inputValues[3]) }; local to = nil; if (inputValues[4] == "l") then to = { ["x"] = tonumber(inputValues[2]), ["y"] = tonumber(inputValues[3]) - 1 }; elseif (inputValues[4] == "r") then to = { ["x"] = tonumber(inputValues[2]), ["y"] = tonumber(inputValues[3]) + 1 }; elseif (inputValues[4] == "u") then to = { ["x"] = tonumber(inputValues[2]) - 1, ["y"] = tonumber(inputValues[3]) }; elseif (inputValues[4] == "d") then to = { ["x"] = tonumber(inputValues[2]) + 1, ["y"] = tonumber(inputValues[3]) }; end -- тут следует добавить оповещение для игрока, если ход некорректный move(from, to); dump(); end input = io.read("*line"); end