--[[ rocheese.luau - RoChess! engine panel + autopilot Luau, Real executor, client side. Uses the game's own modules instead of re-implementing chess: ReplicatedStorage.Chess.Engine board, legal moves, UCI/SAN, FEN ReplicatedStorage.Chess.Bot local search: analyse(fen,moves,sec,depth) / think(fen,moves,persona) ReplicatedStorage.Chess.VortexBot server engine bridge: analyse / think ReplicatedStorage.ChessClient.App live client state: session, board, onBoardMove, playMove Squares use Engine.algToIndex64 (bottom-up, a1 = 1, h8 = 64). Engine scores are always from WHITE's side of the board. Panel: top-right, drag it by the title. Arrows on the board: green = best move blue = second best yellow = third best Presets: LEGIT local engine, plays like a 1500 human (noise + blunders), 5-10s per move RAJ server engine (Vortex), always the best move, plays fast Controls: STYLE, DELAY min/max, PLAY BEST/ELO, ENGINE LOCAL/VORTEX, AUTOPLAY, PLAY NOW, COPY FEN, BOT GAME. Hotkeys: End auto | Home arrow | PageUp hide | PageDown source | Insert copy fen. ]] local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local LocalPlayer = Players.LocalPlayer local Chess = ReplicatedStorage:WaitForChild("Chess") local Client = ReplicatedStorage:WaitForChild("ChessClient") local function req(mod) return require(mod) end local Engine = req(Chess:WaitForChild("Engine")) local Bot = req(Chess:WaitForChild("Bot")) local VortexBot = req(Chess:WaitForChild("VortexBot")) local App = req(Client:WaitForChild("App")) local GENV = getgenv() GENV.__RoCheeseGen = (GENV.__RoCheeseGen or 0) + 1 local GEN = GENV.__RoCheeseGen local CFG = { W = 344, H = 424, MARGIN = 14, TOP = 84, DEPTH = 16, SECONDS = 0.8, CHILD_DEPTH = 8, CHILD_SECONDS = 0.06, VK_SHIFT = 160, PROMO = { q = 5, r = 4, b = 3, n = 2 }, ARROW_1 = Color3.fromRGB(129, 182, 76), ARROW_2 = Color3.fromRGB(96, 156, 240), ARROW_3 = Color3.fromRGB(240, 200, 70), DELAY_MIN = 0, DELAY_MAX = 20, DELAY_STEP = 0.5, } local PRESETS = { legit = { label = "LEGIT", source = "local", mode = "elo", elo = 1500, noise = 0.32, blunder = 0.06, delayMin = 5, delayMax = 10, }, raj = { label = "RAJ", source = "vortex", mode = "best", elo = 1800, noise = 0, blunder = 0, delayMin = 0.5, delayMax = 1.5, }, } local RUNTIME = GENV.__RoCheese or {} RUNTIME.SHOW_ARROW = RUNTIME.SHOW_ARROW ~= false RUNTIME.AUTO = RUNTIME.AUTO == true RUNTIME.HUMANIZE = RUNTIME.HUMANIZE ~= false RUNTIME.SOURCE = RUNTIME.SOURCE == "vortex" and "vortex" or "local" RUNTIME.MODE = RUNTIME.MODE == "best" and "best" or "elo" RUNTIME.ELO = tonumber(RUNTIME.ELO) or 1500 RUNTIME.NOISE = tonumber(RUNTIME.NOISE) or 0.32 RUNTIME.BLUNDER = tonumber(RUNTIME.BLUNDER) or 0.06 RUNTIME.DELAY_MIN = math.clamp(tonumber(RUNTIME.DELAY_MIN) or 5, CFG.DELAY_MIN, CFG.DELAY_MAX) RUNTIME.DELAY_MAX = math.clamp(tonumber(RUNTIME.DELAY_MAX) or 10, CFG.DELAY_MIN, CFG.DELAY_MAX) RUNTIME.STYLE = RUNTIME.STYLE == "raj" and "raj" or (RUNTIME.STYLE == "legit" and "legit" or "custom") GENV.__RoCheese = RUNTIME local ARROW_KEYS = { "rc1", "rc2", "rc3" } local S = { ply = -1, sessionId = nil, best = nil, san = nil, score = 0, depth = 0, second = nil, third = nil, secondSan = nil, thirdSan = nil, busy = false, autoAt = 0, thinkAt = 0, status = "loading", } GENV.__RoCheeseState = S local LIVE = _G.STATE local function alive() if GENV.__RoCheeseGen ~= GEN then return false end if typeof(LIVE) == "table" and type(LIVE.alive) == "function" then return LIVE.alive() end return true end local function cleanup(fn) if typeof(LIVE) == "table" and type(LIVE.onCleanup) == "function" then LIVE.onCleanup(fn) end end -- board ------------------------------------------------------------------------------------ local function clearArrows() local board = App.board if not board or type(board.setArrow) ~= "function" then return end for _, key in ARROW_KEYS do pcall(function() board:setArrow(key, nil) board:setTint(key, nil) end) end -- older build of this script used a single arrow key pcall(function() board:setArrow("rocheese", nil) board:setTint("rocheese", nil) end) end local function drawArrow(key, uci, color, tint) local board = App.board if not board then return end local from = Engine.algToIndex64(uci:sub(1, 2)) local to = Engine.algToIndex64(uci:sub(3, 4)) if not from or not to then return end pcall(function() board:setArrow(key, from, to, color, 0.12) if tint then board:setTint(key, to, color, 0.26) end end) end -- engine ---------------------------------------------------------------------------------- local function buildEngine(startFen, moves) local engine = Engine.new(startFen) for _, uci in ipairs(moves) do local mv = engine:moveFromUci(uci) if not mv then return nil end engine:play(mv) end return engine end local function sanOf(engine, uci) if not engine or not uci then return nil end local want = string.lower(uci) for _, mv in engine:legalMoves() do local u = engine:moveToUci(mv) if u and string.lower(u) == want then return engine:moveToSan(mv) end end return nil end local function analyseBest(startFen, moves) if RUNTIME.SOURCE == "vortex" then local ok, res = pcall(VortexBot.analyse, startFen, moves, CFG.SECONDS, CFG.DEPTH) if ok and type(res) == "table" and res.best then return res end end local ok, res = pcall(Bot.analyse, startFen, moves, CFG.SECONDS, CFG.DEPTH) if ok and type(res) == "table" then return res end return nil end -- ranks every continuation of the position, so the 2nd and 3rd arrows have a real order local function rankContinuations(startFen, moves, skipUci) local engine = buildEngine(startFen, moves) if not engine then return nil end local scored = {} for _, mv in engine:legalMoves() do local uci = engine:moveToUci(mv) if not skipUci or string.lower(uci) ~= string.lower(skipUci) then local child = Engine.new(startFen) local ok = pcall(function() child:playUci(uci) end) if ok then local cres = Bot.analyse(child:getFen(), {}, CFG.CHILD_SECONDS, CFG.CHILD_DEPTH) table.insert(scored, { uci = uci, san = engine:moveToSan(mv), score = tonumber(cres and cres.score) or 0, }) end end end table.sort(scored, function(a, b) return a.score > b.score end) return scored end local function pickMove(session) if RUNTIME.MODE == "elo" then local ok, uci = pcall(Bot.think, session.startFen, table.clone(session.ucis), { rating = RUNTIME.ELO, noise = RUNTIME.NOISE, blunder = RUNTIME.BLUNDER, time = 0.6, depth = 14, }) if ok and type(uci) == "string" then return uci end end return S.best end -- playing --------------------------------------------------------------------------------- local function tapInput() if not RUNTIME.HUMANIZE then return end if type(keyclick) ~= "function" then return end if type(isrbxactive) == "function" and not isrbxactive() then return end pcall(keyclick, CFG.VK_SHIFT) end local function playUci(uci) local session = App.session if not session or session.over then return false, "no game" end if not session:isMyTurn() then return false, "not your turn" end local from = Engine.algToIndex64(uci:sub(1, 2)) local to = Engine.algToIndex64(uci:sub(3, 4)) if not from or not to then return false, "bad uci" end local promo = CFG.PROMO[uci:sub(5, 5)] or 0 local before = #session.ucis tapInput() local ok = false pcall(function() -- same call a square click makes: local apply + packed move to the server ok = App.onBoardMove(from, to, promo, nil) end) if not ok or #session.ucis == before then pcall(function() App.playMove(uci) end) end return true, uci end local function humanDelay() local lo = math.min(RUNTIME.DELAY_MIN, RUNTIME.DELAY_MAX) local hi = math.max(RUNTIME.DELAY_MIN, RUNTIME.DELAY_MAX) if hi - lo < 0.2 then return lo end return lo + math.random() * (hi - lo) end local function autopilotTick(session, ply) if not RUNTIME.AUTO then return end if session.over or not session:isMyTurn() then return end if session.viewPly ~= ply or S.busy or not S.best then return end local now = os.clock() if now < S.thinkAt then return end local uci = pickMove(session) if type(uci) ~= "string" then return end task.spawn(function() if not alive() then return end if App.session == session and not session.over and session:isMyTurn() then playUci(uci) end end) end -- gui ------------------------------------------------------------------------------------- local gui = nil local panel = nil local rows = {} local btns = {} local dot = nil local dragged = false local lastEval = nil -- forward declarations: the buttons are wired before these are defined local toggleAuto, playNow, copyPosition, refreshButtons, syncDelay, applyPreset, startBotGame local C_BG = Color3.fromRGB(17, 18, 22) local C_IDLE = Color3.fromRGB(36, 39, 46) local C_ON = Color3.fromRGB(52, 104, 60) local C_RAJ = Color3.fromRGB(148, 54, 48) local C_TXT = Color3.fromRGB(236, 238, 241) local C_DIM = Color3.fromRGB(160, 166, 176) local C_ACC = Color3.fromRGB(255, 170, 0) local function mkText(parent, y, h, size, color, bold, x) local t = Instance.new("TextLabel") t.BackgroundTransparency = 1 t.Size = UDim2.fromOffset(CFG.W - 28 - (x or 14), h) t.Position = UDim2.fromOffset(x or 14, y) t.Font = bold and Enum.Font.GothamBold or Enum.Font.Gotham t.TextSize = size t.TextColor3 = color t.TextXAlignment = Enum.TextXAlignment.Left t.TextYAlignment = Enum.TextYAlignment.Center t.TextTruncate = Enum.TextTruncate.AtEnd t.Parent = parent return t end local function mkButton(y, h, text, width, onClick) local b = Instance.new("TextButton") b.BackgroundColor3 = C_IDLE b.BorderSizePixel = 0 b.AutoButtonColor = false b.Size = UDim2.fromOffset(width or (CFG.W - 28), h) b.Position = UDim2.fromOffset(14, y) b.Font = Enum.Font.GothamBold b.TextSize = 12 b.TextColor3 = C_TXT b.Text = text b.Parent = panel local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, 6) c.Parent = b local s = Instance.new("UIStroke") s.Color = Color3.fromRGB(255, 255, 255) s.Thickness = 1 s.Transparency = 0.85 s.Parent = b b.MouseButton1Click:Connect(onClick) b.MouseEnter:Connect(function() s.Transparency = 0.45 end) b.MouseLeave:Connect(function() s.Transparency = 0.85 end) return b end local function setBtn(b, on, text, onColor) b.BackgroundColor3 = on and (onColor or C_ON) or C_IDLE b.Text = text end local function mkSeparator(y) local sep = Instance.new("Frame") sep.BackgroundColor3 = Color3.fromRGB(255, 255, 255) sep.BackgroundTransparency = 0.87 sep.BorderSizePixel = 0 sep.Size = UDim2.fromOffset(CFG.W - 28, 1) sep.Position = UDim2.fromOffset(14, y) sep.Parent = panel return sep end local function mkDot(x, y, color, size) local d = Instance.new("Frame") d.BackgroundColor3 = color d.BorderSizePixel = 0 d.Size = UDim2.fromOffset(size or 9, size or 9) d.Position = UDim2.fromOffset(x, y) d.Parent = panel local c = Instance.new("UICorner") c.CornerRadius = UDim.new(1, 0) c.Parent = d return d end -- delay row: MIN / MAX steppers, every press is one step, no dragging ---------------------- local function mkDelayRow(y) local row = Instance.new("Frame") row.BackgroundTransparency = 1 row.Size = UDim2.fromOffset(CFG.W - 28, 26) row.Position = UDim2.fromOffset(14, y) row.Parent = panel local title = Instance.new("TextLabel") title.BackgroundTransparency = 1 title.Size = UDim2.fromOffset(42, 26) title.Text = "DELAY" title.Font = Enum.Font.GothamBold title.TextSize = 11 title.TextColor3 = C_DIM title.TextXAlignment = Enum.TextXAlignment.Left title.Parent = row local values = {} local function stepper(x, name, getVal, setVal) local lab = Instance.new("TextLabel") lab.BackgroundTransparency = 1 lab.Size = UDim2.fromOffset(28, 26) lab.Position = UDim2.fromOffset(x, 0) lab.Text = name lab.Font = Enum.Font.GothamBold lab.TextSize = 11 lab.TextColor3 = C_DIM lab.TextXAlignment = Enum.TextXAlignment.Left lab.Parent = row local function smallButton(px, sign) local b = Instance.new("TextButton") b.BackgroundColor3 = C_IDLE b.BorderSizePixel = 0 b.AutoButtonColor = false b.Size = UDim2.fromOffset(22, 22) b.Position = UDim2.fromOffset(px, 2) b.Font = Enum.Font.GothamBold b.TextSize = 13 b.TextColor3 = C_TXT b.Text = sign b.Parent = row local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, 5) c.Parent = b local s = Instance.new("UIStroke") s.Color = Color3.fromRGB(255, 255, 255) s.Thickness = 1 s.Transparency = 0.85 s.Parent = b local holding = false local function step() setVal(getVal() + (sign == "+" and CFG.DELAY_STEP or -CFG.DELAY_STEP)) end b.MouseButton1Down:Connect(function() holding = true step() task.spawn(function() task.wait(0.35) local reps = 0 while holding and reps < 60 do step() reps += 1 task.wait(0.05) end end) end) b.MouseButton1Up:Connect(function() holding = false end) b.MouseLeave:Connect(function() holding = false end) return b end local value = Instance.new("TextLabel") value.BackgroundTransparency = 1 value.Size = UDim2.fromOffset(38, 26) value.Position = UDim2.fromOffset(x + 52, 0) value.Font = Enum.Font.GothamBold value.TextSize = 12 value.TextColor3 = C_TXT value.TextXAlignment = Enum.TextXAlignment.Center value.Parent = row values[name] = value smallButton(x + 30, "-") smallButton(x + 92, "+") end stepper(58, "MIN", function() return RUNTIME.DELAY_MIN end, function(v) RUNTIME.DELAY_MIN = math.clamp(v, CFG.DELAY_MIN, RUNTIME.DELAY_MAX - CFG.DELAY_STEP) RUNTIME.STYLE = "custom" syncDelay() refreshButtons() end) stepper(176, "MAX", function() return RUNTIME.DELAY_MAX end, function(v) RUNTIME.DELAY_MAX = math.clamp(v, RUNTIME.DELAY_MIN + CFG.DELAY_STEP, CFG.DELAY_MAX) RUNTIME.STYLE = "custom" syncDelay() refreshButtons() end) local function layout() values.MIN.Text = string.format("%.1fs", RUNTIME.DELAY_MIN) values.MAX.Text = string.format("%.1fs", RUNTIME.DELAY_MAX) end layout() return layout end local function buildGui() local playerGui = LocalPlayer:WaitForChild("PlayerGui") local old = playerGui:FindFirstChild("RoCheesePanel") if old then old:Destroy() end gui = Instance.new("ScreenGui") gui.Name = "RoCheesePanel" gui.ResetOnSpawn = false gui.IgnoreGuiInset = true gui.DisplayOrder = 999 gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling gui.Parent = playerGui panel = Instance.new("Frame") panel.Name = "Panel" panel.BackgroundColor3 = C_BG panel.BackgroundTransparency = 0.04 panel.BorderSizePixel = 0 panel.Size = UDim2.fromOffset(CFG.W, CFG.H) panel.Position = UDim2.fromOffset(1920 - CFG.W - CFG.MARGIN, CFG.TOP) panel.Active = true panel.Parent = gui local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 12) corner.Parent = panel local stroke = Instance.new("UIStroke") stroke.Color = C_ACC stroke.Thickness = 1 stroke.Transparency = 0.45 stroke.Parent = panel rows.title = mkText(panel, 10, 22, 15, C_ACC, true) rows.title.Size = UDim2.fromOffset(CFG.W - 56, 22) dot = mkDot(CFG.W - 32, 17, C_DIM) rows.status = mkText(panel, 34, 16, 11, C_DIM) rows.eval = mkText(panel, 54, 20, 14, C_TXT, true) rows.best = mkText(panel, 76, 18, 13, C_TXT) rows.rest = mkText(panel, 94, 18, 12, C_DIM) rows.game = mkText(panel, 114, 18, 11, C_DIM) mkSeparator(136) local half = math.floor((CFG.W - 32) / 2) btns.legit = mkButton(146, 30, "STYLE LEGIT", half, function() applyPreset("legit") end) btns.raj = mkButton(146, 30, "STYLE RAJ", half, function() applyPreset("raj") end) btns.raj.Position = UDim2.fromOffset(18 + half, 146) syncDelay = mkDelayRow(182) btns.best = mkButton(214, 26, "PLAY BEST", half, function() RUNTIME.MODE = RUNTIME.MODE == "best" and "elo" or "best" RUNTIME.STYLE = "custom" refreshButtons() end) btns.elo = mkButton(214, 26, "PLAY ELO", half, function() RUNTIME.MODE = RUNTIME.MODE == "elo" and "best" or "elo" RUNTIME.STYLE = "custom" refreshButtons() end) btns.elo.Position = UDim2.fromOffset(18 + half, 214) btns.engineLocal = mkButton(246, 26, "ENGINE LOCAL", half, function() RUNTIME.SOURCE = RUNTIME.SOURCE == "local" and "vortex" or "local" RUNTIME.STYLE = "custom" S.ply = -1 refreshButtons() end) btns.engineVortex = mkButton(246, 26, "ENGINE VORTEX", half, function() RUNTIME.SOURCE = RUNTIME.SOURCE == "vortex" and "local" or "vortex" RUNTIME.STYLE = "custom" S.ply = -1 refreshButtons() end) btns.engineVortex.Position = UDim2.fromOffset(18 + half, 246) btns.auto = mkButton(280, 34, "AUTOPLAY OFF", nil, function() toggleAuto() end) btns.play = mkButton(320, 26, "PLAY NOW", half, function() playNow() end) btns.copy = mkButton(320, 26, "COPY FEN", half, function() copyPosition() end) btns.copy.Position = UDim2.fromOffset(18 + half, 320) btns.botgame = mkButton(352, 26, "BOT GAME (practice)", nil, function() startBotGame() end) mkSeparator(384) -- legend for the three arrows mkDot(14, 398, CFG.ARROW_1, 9) local l1 = Instance.new("TextLabel") l1.BackgroundTransparency = 1 l1.Size = UDim2.fromOffset(80, 20) l1.Position = UDim2.fromOffset(27, 392) l1.Font = Enum.Font.Gotham l1.TextSize = 11 l1.TextColor3 = C_TXT l1.Text = "1 best" l1.Parent = panel mkDot(112, 398, CFG.ARROW_2, 9) local l2 = Instance.new("TextLabel") l2.BackgroundTransparency = 1 l2.Size = UDim2.fromOffset(90, 20) l2.Position = UDim2.fromOffset(125, 392) l2.Font = Enum.Font.Gotham l2.TextSize = 11 l2.TextColor3 = C_TXT l2.Text = "2 second" l2.Parent = panel mkDot(212, 398, CFG.ARROW_3, 9) local l3 = Instance.new("TextLabel") l3.BackgroundTransparency = 1 l3.Size = UDim2.fromOffset(90, 20) l3.Position = UDim2.fromOffset(225, 392) l3.Font = Enum.Font.Gotham l3.TextSize = 11 l3.TextColor3 = C_TXT l3.Text = "3 third" l3.Parent = panel rows.hint = mkText(panel, 416, 16, 10, Color3.fromRGB(112, 118, 128)) rows.hint.Text = "drag title | End auto | Home arrow | PgUp hide" -- drag the panel by the title local dragging = false local dragFrom = Vector2.zero local dragStart = UDim2.fromOffset(0, 0) rows.title.Active = true rows.title.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true dragged = true dragFrom = Vector2.new(input.Position.X, input.Position.Y) dragStart = panel.Position end end) rows.title.InputChanged:Connect(function(input) if not dragging then return end if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then local cam = workspace.CurrentCamera local vp = cam and cam.ViewportSize or Vector2.new(1920, 1080) local delta = Vector2.new(input.Position.X - dragFrom.X, input.Position.Y - dragFrom.Y) local x = math.clamp(dragStart.X.Offset + delta.X, 0, math.max(0, vp.X - CFG.W)) local y = math.clamp(dragStart.Y.Offset + delta.Y, 0, math.max(0, vp.Y - CFG.H)) panel.Position = UDim2.fromOffset(x, y) end end) rows.title.InputEnded:Connect(function() dragging = false end) cleanup(function() if gui then gui:Destroy() gui = nil panel = nil end end) end refreshButtons = function() if not btns.auto then return end setBtn(btns.auto, RUNTIME.AUTO, RUNTIME.AUTO and "AUTOPLAY ON" or "AUTOPLAY OFF") setBtn(btns.legit, RUNTIME.STYLE == "legit", "STYLE LEGIT", C_ON) setBtn(btns.raj, RUNTIME.STYLE == "raj", "STYLE RAJ", C_RAJ) setBtn(btns.best, RUNTIME.MODE == "best", "PLAY BEST") setBtn(btns.elo, RUNTIME.MODE == "elo", "PLAY ELO " .. RUNTIME.ELO) setBtn(btns.engineLocal, RUNTIME.SOURCE == "local", "ENGINE LOCAL") setBtn(btns.engineVortex, RUNTIME.SOURCE == "vortex", "ENGINE VORTEX") end -- placement ------------------------------------------------------------------------------- local function shiftPanel(dx, dy) if not panel or not panel.Parent then return end local cam = workspace.CurrentCamera local vp = cam and cam.ViewportSize or Vector2.new(1920, 1080) local x = math.clamp(panel.Position.X.Offset + dx, 0, math.max(0, vp.X - CFG.W)) local y = math.clamp(panel.Position.Y.Offset + dy, 0, math.max(0, vp.Y - CFG.H)) panel.Position = UDim2.fromOffset(x, y) dragged = true end local function layoutPanel() if dragged or not panel or not panel.Parent then return end local cam = workspace.CurrentCamera local vp = cam and cam.ViewportSize or Vector2.new(1920, 1080) panel.Position = UDim2.fromOffset(vp.X - CFG.W - CFG.MARGIN, CFG.TOP) end -- actions --------------------------------------------------------------------------------- toggleAuto = function() RUNTIME.AUTO = not RUNTIME.AUTO S.thinkAt = os.clock() + (RUNTIME.AUTO and humanDelay() or 0) S.status = RUNTIME.AUTO and "autopilot on" or "autopilot off" refreshButtons() end playNow = function() local session = App.session if not session then S.status = "no game running" return end if S.busy or not S.best then S.status = "engine still thinking" return end local uci = pickMove(session) if type(uci) ~= "string" then S.status = "no move available" return end local ok, err = playUci(uci) S.status = ok and ("played " .. tostring(err)) or tostring(err) end copyPosition = function() local session = App.session if not session then S.status = "no game running" return end local fen = session.engine:getFen() local sans = table.concat(session.sans, " ") pcall(setclipboard, fen .. "\n" .. sans) S.status = "fen + moves copied" end startBotGame = function() local ok, err = pcall(function() App.startBotGame("jasper", "white") end) S.status = ok and "practice game started" or ("bot game failed: " .. tostring(err)) end applyPreset = function(name) local preset = PRESETS[name] if not preset then return end RUNTIME.STYLE = name RUNTIME.SOURCE = preset.source RUNTIME.MODE = preset.mode RUNTIME.ELO = preset.elo RUNTIME.NOISE = preset.noise RUNTIME.BLUNDER = preset.blunder RUNTIME.DELAY_MIN = preset.delayMin RUNTIME.DELAY_MAX = preset.delayMax S.ply = -1 S.status = name .. " preset" if syncDelay then syncDelay() end refreshButtons() end local setDelay = function(lo, hi) lo = math.clamp(tonumber(lo) or RUNTIME.DELAY_MIN, CFG.DELAY_MIN, CFG.DELAY_MAX) hi = math.clamp(tonumber(hi) or RUNTIME.DELAY_MAX, CFG.DELAY_MIN, CFG.DELAY_MAX) if hi < lo then lo, hi = hi, lo end RUNTIME.DELAY_MIN = math.floor(lo / CFG.DELAY_STEP + 0.5) * CFG.DELAY_STEP RUNTIME.DELAY_MAX = math.floor(hi / CFG.DELAY_STEP + 0.5) * CFG.DELAY_STEP if RUNTIME.DELAY_MAX - RUNTIME.DELAY_MIN < CFG.DELAY_STEP then RUNTIME.DELAY_MAX = RUNTIME.DELAY_MIN + CFG.DELAY_STEP end RUNTIME.STYLE = "custom" if syncDelay then syncDelay() end refreshButtons() end -- hotkeys --------------------------------------------------------------------------------- local function onKey(input, processed) if processed or UserInputService:GetFocusedTextBox() then return end local code = input.KeyCode if code == Enum.KeyCode.End then toggleAuto() elseif code == Enum.KeyCode.Home then RUNTIME.SHOW_ARROW = not RUNTIME.SHOW_ARROW if not RUNTIME.SHOW_ARROW then clearArrows() end refreshButtons() elseif code == Enum.KeyCode.PageUp then if gui then gui.Enabled = not gui.Enabled end elseif code == Enum.KeyCode.PageDown then RUNTIME.SOURCE = RUNTIME.SOURCE == "local" and "vortex" or "local" RUNTIME.STYLE = "custom" S.ply = -1 refreshButtons() elseif code == Enum.KeyCode.Insert then copyPosition() end end local prevConn = GENV.__RoCheeseConn if prevConn then pcall(function() prevConn:Disconnect() end) end local conn = UserInputService.InputBegan:Connect(onKey) GENV.__RoCheeseConn = conn cleanup(function() conn:Disconnect() end) -- eval bar ------------------------------------------------------------------------------- -- the game already ships a chess-style eval bar next to the board, it is just never -- switched on during a normal game. Feeding it our score wakes it up. local GAME_KINDS = { bot = true, online = true, puzzle = true, rush = true, } local function pushEval() local session = App.session local kind = session and session.kind if not session or not kind or not GAME_KINDS[kind] then lastEval = nil return end if S.busy or not S.best then return end local score = S.score if lastEval == score then return end lastEval = score local mate = nil if math.abs(score) >= 29900 then mate = math.max(1, math.floor((30000 - math.abs(score)) / 100 + 0.5)) end pcall(function() App.setEval(score, mate) end) end -- panel refresh --------------------------------------------------------------------------- local function formatClock(ms) if type(ms) ~= "number" then return "-" end ms = math.max(0, ms) local total = math.floor(ms / 1000) return string.format("%d:%02d", math.floor(total / 60), total % 60) end local function evalText(score) local abs = math.abs(score) if abs >= 29900 then local moves = math.max(1, math.floor((30000 - abs) / 100 + 0.5)) return string.format("M%d %s", moves, score > 0 and "white" or "black") end local pawns = score / 100 return string.format("%s%.2f", pawns >= 0 and "+" or "", pawns) end local function refreshPanel(session, ply) if not panel or not panel.Parent then return end rows.title.Text = "ROCHESS ENGINE" rows.status.Text = S.status rows.eval.Text = session and (S.busy and "EVAL ... DEPTH ..." or string.format("EVAL %s DEPTH %d", evalText(S.score), S.depth)) or "EVAL - DEPTH 0" if S.busy then rows.best.Text = "1 thinking..." rows.rest.Text = "" else rows.best.Text = S.best and string.format("1 %s (%s)", S.san or "?", S.best) or "1 -" local parts = {} if S.second then table.insert(parts, string.format("2 %s", S.secondSan or S.second)) end if S.third then table.insert(parts, string.format("3 %s", S.thirdSan or S.third)) end rows.rest.Text = table.concat(parts, " ") end local myClock, oppClock = "-", "-" if session and session.clocks and session.myColor then myClock = formatClock(session.clocks[session.myColor]) oppClock = formatClock(session.clocks[-session.myColor]) end rows.game.Text = session and string.format("%s | ply %d | clock %s/%s", session.myColor == 1 and (session.white and session.white.display or "white") or (session.black and session.black.display or "black"), ply, myClock, oppClock) or "no game" if session and session.kind == "idle" then rows.status.Text = "no game running - press BOT GAME or Play in the game" end if dot then if S.busy then dot.BackgroundColor3 = C_ACC elseif S.best then dot.BackgroundColor3 = CFG.ARROW_1 else dot.BackgroundColor3 = Color3.fromRGB(200, 80, 70) end end pushEval() end -- main loop ------------------------------------------------------------------------------- buildGui() refreshButtons() S.status = "ready" GENV.__RoCheese.api = { auto = toggleAuto, play = playNow, copy = copyPosition, move = shiftPanel, delay = setDelay, preset = applyPreset, state = function() return { ply = S.ply, best = S.best, san = S.san, second = S.second, third = S.third, score = S.score, depth = S.depth, status = S.status, auto = RUNTIME.AUTO, style = RUNTIME.STYLE, source = RUNTIME.SOURCE, mode = RUNTIME.MODE, delayMin = RUNTIME.DELAY_MIN, delayMax = RUNTIME.DELAY_MAX, } end, } task.spawn(function() while alive() do local session = App.session local ply = session and #session.ucis or -1 local sid = session and session.id or nil if session and sid ~= S.sessionId then S.sessionId = sid lastEval = nil pcall(function() Bot.clearTT() end) end if session and ply ~= S.ply then S.ply = ply -- a new position means a fresh think time, exactly like a human S.thinkAt = os.clock() + humanDelay() S.best = nil S.san = nil S.second = nil S.third = nil S.secondSan = nil S.thirdSan = nil clearArrows() if not S.busy then S.busy = true local startFen = session.startFen local moves = table.clone(session.ucis) local wantPly = ply task.spawn(function() -- main search: the best move and the eval local res = analyseBest(startFen, moves) if not alive() then return end S.busy = false local live = App.session if not live or #live.ucis ~= wantPly then S.ply = -1 return end if type(res) ~= "table" then S.status = "engine failed" return end S.best = res.best S.score = tonumber(res.score) or 0 S.depth = tonumber(res.depth) or 0 S.status = "engine ready" local engine = buildEngine(startFen, moves) S.san = sanOf(engine, res.best) -- second pass: rank the continuations for the 2nd and 3rd arrows task.spawn(function() local ranked = rankContinuations(startFen, moves, res.best) if not alive() then return end local live2 = App.session if not live2 or #live2.ucis ~= wantPly or not ranked then return end S.second = ranked[1] and ranked[1].uci or nil S.secondSan = ranked[1] and ranked[1].san or nil S.third = ranked[2] and ranked[2].uci or nil S.thirdSan = ranked[2] and ranked[2].san or nil end) end) end end if session then local live = RUNTIME.SHOW_ARROW and session.viewPly == ply and not session.over if live then if S.best then drawArrow(ARROW_KEYS[1], S.best, CFG.ARROW_1, true) end if S.second then drawArrow(ARROW_KEYS[2], S.second, CFG.ARROW_2, false) end if S.third then drawArrow(ARROW_KEYS[3], S.third, CFG.ARROW_3, false) end else clearArrows() end autopilotTick(session, ply) end refreshPanel(session, ply) layoutPanel() task.wait(0.12) end end) cleanup(clearArrows) print("[rocheese] ready - STYLE LEGIT/RAJ, DELAY min/max, AUTOPLAY; arrows: green best / blue second / yellow third")