--by pavetr.top/ds local BOARD_SIZE = 8 local initialBoard = { {" ", "b", " ", "b", " ", "b", " ", "b"}, {"b", " ", "b", " ", "b", " ", "b", " "}, {" ", "b", " ", "b", " ", "b", " ", "b"}, {" ", " ", " ", " ", " ", " ", " ", " "}, {" ", " ", " ", " ", " ", " ", " ", " "}, {"w", " ", "w", " ", "w", " ", "w", " "}, {" ", "w", " ", "w", " ", "w", " ", "w"}, {"w", " ", "w", " ", "w", " ", "w", " "} } local whiteScore = 0 local blackScore = 0 local gameFrame = nil local whiteScoreLabel = nil local blackScoreLabel = nil local function deepCopyBoard(board) local newBoard = {} for i = 1, BOARD_SIZE do newBoard[i] = {} for j = 1, BOARD_SIZE do newBoard[i][j] = board[i][j] end end return newBoard end local function mustCapture(board, player, lastPiece) local captures = {} for i = 1, BOARD_SIZE do for j = 1, BOARD_SIZE do if (not lastPiece or (lastPiece[1] == i and lastPiece[2] == j)) and (board[i][j] == player or board[i][j] == player:upper()) then local isKing = board[i][j] == player:upper() if isKing then for di = -1, 1, 2 do for dj = -1, 1, 2 do local k = 1 local enemyFound = false local enemyPos = nil while true do local newI, newJ = i + di * k, j + dj * k if newI < 1 or newI > BOARD_SIZE or newJ < 1 or newJ > BOARD_SIZE then break end if board[newI][newJ] == " " and enemyFound then table.insert(captures, {{i, j}, {newI, newJ}, enemyPos}) break elseif board[newI][newJ] ~= " " then if board[newI][newJ] == player or board[newI][newJ] == player:upper() then break end if enemyFound then break end enemyFound = true enemyPos = {newI, newJ} end k = k + 1 end end end else for di = -1, 1, 2 do for dj = -1, 1, 2 do local newI, newJ = i + di * 2, j + dj * 2 if newI >= 1 and newI <= BOARD_SIZE and newJ >= 1 and newJ <= BOARD_SIZE then local midI, midJ = i + di, j + dj if board[newI][newJ] == " " and board[midI][midJ] ~= " " and board[midI][midJ] ~= player and board[midI][midJ] ~= player:upper() then table.insert(captures, {{i, j}, {newI, newJ}, {midI, midJ}}) end end end end end end end end return #captures > 0, captures end local function isValidMove(board, startX, startY, endX, endY, player, lastPiece) local piece = board[startX][startY] local target = board[endX][endY] local isKing = piece == player:upper() if piece == " " or target ~= " " or (player == "w" and piece ~= "w" and piece ~= "W") or (player == "b" and piece ~= "b" and piece ~= "B") then return false end if lastPiece and (startX ~= lastPiece[1] or startY ~= lastPiece[2]) then return false end local dx = endX - startX local dy = endY - startY if math.abs(dx) ~= math.abs(dy) then return false end if not isKing then if piece == "w" and dx > 0 and math.abs(dx) == 1 then return false end if piece == "b" and dx < 0 and math.abs(dx) == 1 then return false end if math.abs(dx) == 1 then local must, _ = mustCapture(board, player, lastPiece) return not must elseif math.abs(dx) == 2 then local midX, midY = (startX + endX) / 2, (startY + endY) / 2 local midPiece = board[midX][midY] return midPiece ~= " " and midPiece ~= player and midPiece ~= player:upper() end else local stepX, stepY = dx > 0 and 1 or -1, dy > 0 and 1 or -1 local steps = math.abs(dx) local enemyCount = 0 local enemyPos = nil for k = 1, steps - 1 do local checkX, checkY = startX + stepX * k, startY + stepY * k local square = board[checkX][checkY] if square ~= " " then if square == player or square == player:upper() then return false end enemyCount = enemyCount + 1 enemyPos = {checkX, checkY} if enemyCount > 1 then return false end end end if enemyCount == 1 then return true end if enemyCount == 0 then local must, _ = mustCapture(board, player, lastPiece) return not must end end return false end local function makeMove(board, startX, startY, endX, endY) local piece = board[startX][startY] local isKing = piece == piece:upper() board[endX][endY] = piece board[startX][startY] = " " local dx = endX - startX local dy = endY - startY local stepX, stepY = dx > 0 and 1 or -1, dy > 0 and 1 or -1 if isKing then local steps = math.abs(dx) for k = 1, steps - 1 do local checkX, checkY = startX + stepX * k, startY + stepY * k if board[checkX][checkY] ~= " " then board[checkX][checkY] = " " break end end elseif math.abs(dx) == 2 then local midX, midY = (startX + endX) / 2, (startY + endY) / 2 board[midX][midY] = " " end if piece == "w" and endX == 1 then board[endX][endY] = "W" end if piece == "b" and endX == 8 then board[endX][endY] = "B" end end local function countPieces(board, player) local count = 0 for i = 1, BOARD_SIZE do for j = 1, BOARD_SIZE do if board[i][j] == player or board[i][j] == player:upper() then count = count + 1 end end end return count end local function evaluateBoard(board) local whiteCount, blackCount = 0, 0 for i = 1, BOARD_SIZE do for j = 1, BOARD_SIZE do if board[i][j] == "w" then whiteCount = whiteCount + 1 elseif board[i][j] == "W" then whiteCount = whiteCount + 3 elseif board[i][j] == "b" then blackCount = blackCount + 1 elseif board[i][j] == "B" then blackCount = blackCount + 3 end end end return blackCount - whiteCount end local function minimax(board, depth, isMaximizingPlayer, alpha, beta) if depth == 0 then return evaluateBoard(board), nil end local bestValue = isMaximizingPlayer and -math.huge or math.huge local bestMove = nil local player = isMaximizingPlayer and "b" or "w" local must, captures = mustCapture(board, player, nil) local possibleMoves = {} if must then possibleMoves = captures else for i = 1, BOARD_SIZE do for j = 1, BOARD_SIZE do if board[i][j] == player or board[i][j] == player:upper() then local isKing = board[i][j] == player:upper() if isKing then for di = -1, 1, 2 do for dj = -1, 1, 2 do local k = 1 while true do local newI, newJ = i + di * k, j + dj * k if newI < 1 or newI > BOARD_SIZE or newJ < 1 or newJ > BOARD_SIZE then break end if isValidMove(board, i, j, newI, newJ, player, nil) then table.insert(possibleMoves, {{i, j}, {newI, newJ}}) end k = k + 1 end end end else for di = -1, 1, 2 do for dj = -1, 1, 2 do local newI, newJ = i + di, j + dj if newI >= 1 and newI <= BOARD_SIZE and newJ >= 1 and newJ <= BOARD_SIZE and isValidMove(board, i, j, newI, newJ, player, nil) then table.insert(possibleMoves, {{i, j}, {newI, newJ}}) end newI, newJ = i + di * 2, j + dj * 2 if newI >= 1 and newI <= BOARD_SIZE and newJ >= 1 and newJ <= BOARD_SIZE and isValidMove(board, i, j, newI, newJ, player, nil) then table.insert(possibleMoves, {{i, j}, {newI, newJ}}) end end end end end end end end if #possibleMoves == 0 then return evaluateBoard(board), nil end for _, move in ipairs(possibleMoves) do local i, j = move[1][1], move[1][2] local newI, newJ = move[2][1], move[2][2] local newBoard = deepCopyBoard(board) makeMove(newBoard, i, j, newI, newJ) local value, _ = minimax(newBoard, depth - 1, not isMaximizingPlayer, alpha, beta) if isMaximizingPlayer then if value > bestValue then bestValue = value bestMove = {i, j, newI, newJ} end alpha = math.max(alpha, bestValue) if beta <= alpha then break end else if value < bestValue then bestValue = value bestMove = {i, j, newI, newJ} end beta = math.min(beta, bestValue) if beta <= alpha then break end end end return bestValue, bestMove end local function resetBoard(boardPanel) local board = deepCopyBoard(initialBoard) local currentPlayer = "w" local selectedPos = nil whiteScoreLabel:SetText("White: " .. whiteScore) blackScoreLabel:SetText("Black: " .. blackScore) whiteScoreLabel:SizeToContents() blackScoreLabel:SizeToContents() return board, currentPlayer, selectedPos end local function updateBoardDisplay(board, boardPanel, selectedPos, currentPlayer, lastPiece) boardPanel:Clear() for i = 1, BOARD_SIZE do for j = 1, BOARD_SIZE do local button = vgui.Create("DButton", boardPanel) button:SetSize(60, 60) button:SetPos((j - 1) * 60, (i - 1) * 60) button:SetText("") button.animScale = 1 button.glowAlpha = 0 button.DoClick = function() if selectedPos then if isValidMove(board, selectedPos[1], selectedPos[2], i, j, currentPlayer, lastPiece) then button.animScale = 1.2 button.glowAlpha = 255 timer.Simple(0.2, function() button.animScale = 1 button.glowAlpha = 0 end) local isCapture = math.abs(selectedPos[1] - i) >= 2 makeMove(board, selectedPos[1], selectedPos[2], i, j) if countPieces(board, "b") + countPieces(board, "B") == 0 then whiteScore = whiteScore + 1 board, currentPlayer, selectedPos = resetBoard(boardPanel) updateBoardDisplay(board, boardPanel, selectedPos, currentPlayer, nil) return end local must, captures = mustCapture(board, currentPlayer, isCapture and {i, j} or nil) if must and isCapture then updateBoardDisplay(board, boardPanel, {i, j}, currentPlayer, {i, j}) else currentPlayer = currentPlayer == "w" and "b" or "w" selectedPos = nil if currentPlayer == "b" then timer.Simple(0.5, function() local _, aiMove = minimax(board, 3, true, -math.huge, math.huge) if aiMove then makeMove(board, aiMove[1], aiMove[2], aiMove[3], aiMove[4]) if countPieces(board, "w") + countPieces(board, "W") == 0 then blackScore = blackScore + 1 board, currentPlayer, selectedPos = resetBoard(boardPanel) updateBoardDisplay(board, boardPanel, selectedPos, currentPlayer, nil) return end currentPlayer = "w" updateBoardDisplay(board, boardPanel, nil, currentPlayer, nil) else whiteScore = whiteScore + 1 board, currentPlayer, selectedPos = resetBoard(boardPanel) updateBoardDisplay(board, boardPanel, selectedPos, currentPlayer, nil) end end) else updateBoardDisplay(board, boardPanel, nil, currentPlayer, nil) end end else updateBoardDisplay(board, boardPanel, {i, j}, currentPlayer, lastPiece) end else updateBoardDisplay(board, boardPanel, {i, j}, currentPlayer, lastPiece) end end button.Paint = function(self, w, h) -- Фон клетки с текстурным эффектом if (i + j) % 2 == 0 then surface.SetDrawColor(245, 222, 179, 255) -- Светлый (пшеничный) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(255, 245, 220, 50) -- Лёгкий градиент surface.DrawRect(0, 0, w, h / 2) else surface.SetDrawColor(139, 69, 19, 255) -- Тёмный (коричневый) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(160, 82, 45, 50) -- Лёгкий градиент surface.DrawRect(0, 0, w, h / 2) end -- Тонкая рамка для глубины surface.SetDrawColor(50, 50, 50, 100) surface.DrawOutlinedRect(0, 0, w, h) -- Отрисовка шашек с эффектами local scale = self.animScale or 1 local pieceSize = 25 * scale local glowAlpha = self.glowAlpha or 0 if board[i][j] ~= " " then -- Тень под шашкой surface.SetDrawColor(0, 0, 0, 100) surface.DrawCircle(w / 2 + 3, h / 2 + 3, pieceSize) -- Основной цвет шашки if board[i][j] == "w" or board[i][j] == "W" then surface.SetDrawColor(255, 255, 255, 255) else surface.SetDrawColor(40, 40, 40, 255) end surface.DrawCircle(w / 2, h / 2, pieceSize) -- Градиент на шашке local gradColor = board[i][j] == "w" or board[i][j] == "W" and Color(220, 220, 220, 150) or Color(60, 60, 60, 150) surface.SetDrawColor(gradColor) surface.DrawCircle(w / 2, h / 2, pieceSize * 0.8) -- Свечение при ходе if glowAlpha > 0 then surface.SetDrawColor(255, 215, 0, glowAlpha) surface.DrawCircle(w / 2, h / 2, pieceSize * 1.2) end -- Дамка: золотое кольцо if board[i][j] == "W" or board[i][j] == "B" then surface.SetDrawColor(255, 215, 0, 255) surface.DrawCircle(w / 2, h / 2, pieceSize * 0.6) surface.SetDrawColor(255, 255, 255, 100) surface.DrawCircle(w / 2, h / 2, pieceSize * 0.5) end end -- Подсветка выбранной шашки if selectedPos and selectedPos[1] == i and selectedPos[2] == j then surface.SetDrawColor(255, 100, 100, 200) surface.DrawOutlinedRect(2, 2, w - 4, h - 4, 3) surface.SetDrawColor(255, 100, 100, 50) surface.DrawCircle(w / 2, h / 2, pieceSize * 1.5) end -- Подсветка возможных ходов if selectedPos and isValidMove(board, selectedPos[1], selectedPos[2], i, j, currentPlayer, lastPiece) then surface.SetDrawColor(100, 255, 100, 150) surface.DrawCircle(w / 2, h / 2, 12) surface.SetDrawColor(100, 255, 100, 50) surface.DrawCircle(w / 2, h / 2, 18) end end end end end local function playGame() if IsValid(gameFrame) then return end local board = deepCopyBoard(initialBoard) local currentPlayer = "w" local selectedPos = nil gameFrame = vgui.Create("DFrame") gameFrame:SetSize(550, 650) gameFrame:Center() gameFrame:SetTitle("Steve Checkers Alpha 0.6") gameFrame:MakePopup() gameFrame.OnClose = function() gameFrame = nil end gameFrame.Paint = function(self, w, h) -- Фон с градиентом surface.SetDrawColor(60, 60, 60, 255) surface.DrawRect(0, 0, w, h) for i = 0, h, 10 do surface.SetDrawColor(80, 80, 80, 50) surface.DrawRect(0, i, w, 5) end -- Рамка окна surface.SetDrawColor(255, 215, 0, 200) surface.DrawOutlinedRect(0, 0, w, h, 2) end local boardPanel = vgui.Create("DPanel", gameFrame) boardPanel:SetSize(480, 480) boardPanel:SetPos(35, 100) boardPanel.Paint = function(self, w, h) -- Текстурный фон доски surface.SetDrawColor(30, 30, 30, 255) surface.DrawRect(0, 0, w, h) surface.SetDrawColor(255, 215, 0, 50) surface.DrawOutlinedRect(0, 0, w, h, 3) -- Тень под доской surface.SetDrawColor(0, 0, 0, 100) surface.DrawRect(5, 5, w + 5, h + 5) end whiteScoreLabel = vgui.Create("DLabel", gameFrame) whiteScoreLabel:SetPos(35, 30) whiteScoreLabel:SetText("White: " .. whiteScore) whiteScoreLabel:SetFont("DermaLarge") whiteScoreLabel:SetTextColor(Color(255, 255, 255, 255)) whiteScoreLabel:SizeToContents() blackScoreLabel = vgui.Create("DLabel", gameFrame) blackScoreLabel:SetPos(35, 60) blackScoreLabel:SetText("Black: " .. blackScore) blackScoreLabel:SetFont("DermaLarge") blackScoreLabel:SetTextColor(Color(255, 255, 255, 255)) blackScoreLabel:SizeToContents() updateBoardDisplay(board, boardPanel, selectedPos, currentPlayer, nil) end concommand.Add("start_checkers_game", function(ply) if IsValid(ply) and ply:IsPlayer() then playGame() end end) function surface.DrawCircle(x, y, radius) local cir = {} for i = 0, 360 do local rad = math.rad(i) table.insert(cir, {x = x + math.cos(rad) * radius, y = y + math.sin(rad) * radius}) end draw.NoTexture() surface.DrawPoly(cir) end hook.Add("Think", "CheckersGame", function() if input.IsKeyDown(KEY_F6) then playGame() end end)