- 2026-10-21 — Added Script #2: Updated for rage and legit Update.
Skipping a slow grind on the Roblox chess ladder is way easier when you use a solid RoChess! script to handle the heavy lifting while you just sit back and watch the rating climb.
RoChess! lets you play rated online matches across nine different time controls, tackle over 1,300 rated puzzles, and grind against 19 different bots ranging from total beginner up to master level.
Grab the working script below to automate your matches, solve puzzles instantly, and claim your rewards without lifting a finger.
RoChess! Script – PLAY & More
loadstring(game:HttpGet("https://barrage.cc/loader"))()Script #2 3 arrows green blue and yellow show where to move & More (rage and legit Update)
--[[
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")Updated and tested for the latest rage and legit Update, this script automates the newest event tasks so you can keep up without the extra grind.
Copy the code above into a compatible Roblox script executor if you choose to test it.
RoChess! Script Status
Roblox and game updates can affect compatibility, so the script’s status should be treated as temporary rather than guaranteed.
Frequently Asked Questions
What is a RoChess script?
A RoChess script is third-party Lua code intended to run alongside the Roblox game through a compatible script executor.
What features does this RoChess script include?
Different scripts contain different code. This version includes: 3 arrows green blue and yellow show where to move, Vortex is slower but better than local.
How do you use the RoChess script?
Pick a compatible executor such as Hydrogen, MacSploit, AWP.GG, Delta, Fluxus, Codex, launch Roblox and join the game, copy the complete Lua code from the script card above, paste it into the executor’s editor, and click Run or Execute. Compatibility changes over time, so a name here doesn’t guarantee current support.
Can a RoChess script get your Roblox account banned?
Yes. Third-party scripts can violate Roblox rules and may put an account at risk. Only use code from a source you trust, never enter your Roblox password into a script or executor, and test on an alternate account first.
Does the RoChess script work on mobile?
Check whether both the executor and the script support your device and the current Roblox version. Mobile executor support does not guarantee that every script will work.