M
Description
Rochess open source auto play auto win
GAMERoChess!
UPDATEDOct 26, 2026
ACCESSKeyless
PLATFORMDesktop & Mobile
Script source Luau
-- [[ BloxScripts Risk Notice ]]
-- This script has not been verified by BloxScripts.fun. Deal with caution.
--
-- Stay safe:
-- • Never log in on unofficial Roblox sites or lookalike domains.
-- • Real Roblox links use roblox.com (check the .com ending).
-- • Treat fake Roblox login / "claim reward" pages as phishing.
-- [[ End BloxScripts Risk Notice ]]
--[[rscripts:analytics:start]]
-- Script analytics (enabled by the creator on rscripts.net).
-- Runs in its own thread and cannot affect the script below.
task.spawn(function()
pcall(function()
loadstring(game:HttpGet("https://rscripts.net/api/telemetry/v2/client.lua?s=6adf73c6a96a10cd156e9601"))()
end)
end)
--[[rscripts:analytics:end]]
-- RoChess! (place 139717576471404), Fluent control panel.
-- Execute once. F6 pauses/resumes, F7 plays one move while paused, F8 stops.
-- RightControl shows/hides the window. Starts paused; enable Auto-play in Play.
-- Uses RoChess's Vortex engine; finite searches cannot guarantee perfect play.
-- Settings can also be changed through getgenv().RoChessAuto.Config.
-- Loads Fluent from its official release. No per-frame automation work.
local SETTINGS = {
AutoStart = false,
ThinkSeconds = 1.0,
MaxDepth = 30,
UseServerEngine = true,
PollSeconds = 0.2,
RetrySeconds = 5,
}
assert(game.PlaceId == 139717576471404, "This script is for RoChess!")
local env = getgenv()
local previous = env.RoChessAuto
if type(previous) == "table" and type(previous.Stop) == "function" then
previous.Stop()
end
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Input = game:GetService("UserInputService")
local Chess = ReplicatedStorage:WaitForChild("Chess", 15)
local Client = ReplicatedStorage:WaitForChild("ChessClient", 15)
assert(Chess and Client, "RoChess modules are not loaded yet. Try again shortly.")
-- These game-owned modules are discovered at runtime by the executor.
local loadModule = require :: any
local App = loadModule(Client:WaitForChild("App"))
local Engine = loadModule(Chess:WaitForChild("Engine"))
local Bot = loadModule(Chess:WaitForChild("Bot"))
local Vortex = loadModule(Chess:WaitForChild("VortexBot"))
local api = {
Config = SETTINGS,
Enabled = SETTINGS.AutoStart,
Running = true,
Busy = false,
Status = "Starting",
LastMove = nil,
LastAnalysis = nil,
LastError = nil,
MovesPlayed = 0,
}
env.RoChessAuto = api
local connections = {}
local generation = 0
local oneMove = false
local attemptSession = nil
local attemptFen = nil
local attemptTime = 0
local attemptCount = 0
local guiLibrary
local guiToggle
local guiStatus
local guiMove
local guiError
local syncingGui = false
local lastGuiText = {status = "", move = "", error = ""}
local function refreshGui()
if not guiLibrary or guiLibrary.Unloaded then return end
if guiToggle and guiToggle.Value ~= api.Enabled then
syncingGui = true
guiToggle:SetValue(api.Enabled)
syncingGui = false
end
local session = App.session
local gameText = "Open a bot or online game to begin."
if session and (session.kind == "online" or session.kind == "bot") then
local color = if session.myColor == 1 then "White" else "Black"
local turn = if session.over then "Finished" elseif session:isMyTurn() then "Your turn" else "Opponent's turn"
gameText = (if session.kind == "bot" then "Bot game" else "Online game")
.. " | You: " .. color .. " | " .. turn
end
local statusText = api.Status .. "\n" .. gameText
if lastGuiText.status ~= statusText then
guiStatus:SetDesc(statusText)
lastGuiText.status = statusText
end
local analysis = api.LastAnalysis
local detail = if api.LastMove then "Played: " .. api.LastMove else "No move played yet."
if analysis then
detail ..= " | Depth: " .. tostring(analysis.depth or "?")
if analysis.mate then detail ..= " | Mate: " .. tostring(analysis.mate) end
end
detail ..= "\nMoves played: " .. tostring(api.MovesPlayed)
if lastGuiText.move ~= detail then
guiMove:SetDesc(detail)
lastGuiText.move = detail
end
local errorText = api.LastError or (if api.Enabled then "Auto-play is enabled."
elseif oneMove then "One move queued. Waiting for your turn."
else "Ready. Enable Auto-play, or choose Play one move.")
if lastGuiText.error ~= errorText then
guiError:SetDesc(errorText)
lastGuiText.error = errorText
end
end
local function log(message)
print("[RoChessAuto] " .. message)
end
function api.SetEnabled(value)
if not api.Running then return end
api.Enabled = value == true
generation += 1
oneMove = false
if api.Enabled then
attemptSession, attemptFen, attemptCount = nil, nil, 0
end
api.Status = if api.Enabled then "Waiting for your turn" else "Paused"
refreshGui()
log(api.Status)
end
function api.PlayOnce()
if not api.Running then return end
api.Enabled = false
generation += 1
oneMove = true
attemptSession, attemptFen, attemptCount = nil, nil, 0
api.Status = "One move requested"
refreshGui()
end
function api.Stop()
if not api.Running then return end
api.Running = false
api.Enabled = false
oneMove = false
generation += 1
for _, connection in connections do connection:Disconnect() end
table.clear(connections)
api.Status = "Stopped"
local library = guiLibrary
guiLibrary = nil
if library and not library.Unloaded then
pcall(function() library:Destroy() end)
end
log("Stopped. Execute the script again to restart.")
end
-- Separate position copy: the search never changes the live board.
function api.Analyse(startFen, moves, seconds, cancelled)
local history = table.clone(moves or {})
local duration = math.clamp(tonumber(seconds) or 1, 0.1, 3)
local depth = math.clamp(tonumber(SETTINGS.MaxDepth) or 30, 1, 40)
local options = {slice = 0.003, cancelled = cancelled}
local result
if SETTINGS.UseServerEngine then
local ok, response = pcall(Vortex.analyse, startFen, history, duration, depth, options)
if ok and type(response) == "table" then result = response end
end
if cancelled and cancelled() then return nil end
if not result or (not result.best and not result.terminal) then
result = Bot.analyse(startFen, history, duration, depth, options)
end
if cancelled and cancelled() then return nil end
assert(type(result) == "table", "Engine returned no analysis")
if result.best then
local position = Engine.new(startFen)
for _, uci in history do
local move = position:moveFromUci(uci)
assert(move, "Invalid move in the position history")
position:play(move)
end
assert(position:moveFromUci(result.best), "Engine returned an illegal move")
end
return result
end
local function step()
if not api.Running or (not api.Enabled and not oneMove) then return end
local session = App.session
if not session or (session.kind ~= "online" and session.kind ~= "bot") then
api.Status = "Waiting for a game"
return
end
if session.over then api.Status = "Game finished"; return end
if session.viewPly ~= #session.ucis then
api.Status = "Waiting for the live board (go to the latest move)"
return
end
if not session:isMyTurn() then api.Status = "Opponent's turn"; return end
if session:status() ~= "ongoing" then api.Status = "Game finished"; return end
local fen = session.engine:getFen()
if attemptSession ~= session or attemptFen ~= fen then
attemptSession, attemptFen, attemptCount = session, fen, 0
elseif os.clock() - attemptTime < SETTINGS.RetrySeconds then
return
end
if attemptCount >= 2 then
api.LastError = "The board did not advance after two attempts. Paused."
api.SetEnabled(false)
warn("[RoChessAuto] " .. api.LastError)
return
end
attemptCount += 1
attemptTime = os.clock()
local token = generation
local ply = #session.ucis
local function cancelled()
return not api.Running or token ~= generation
or (not api.Enabled and not oneMove)
or App.session ~= session or session.over
or #session.ucis ~= ply or session.viewPly ~= ply
or not session:isMyTurn() or session.engine:getFen() ~= fen
end
api.Status = "Thinking"
local seconds = math.clamp(tonumber(SETTINGS.ThinkSeconds) or 1, 0.1, 3)
if session.clocks and ply >= 2 then
local remaining = session:clockFor(session.myColor, workspace:GetServerTimeNow()) / 1000
seconds = math.min(seconds, math.max(0.1, remaining * 0.03))
end
local result = api.Analyse(session.startFen, session.ucis, seconds, cancelled)
if cancelled() or not result then return end
if not result.best then api.Status = result.terminal or "No legal move"; return end
api.LastAnalysis = result
local move = session.engine:moveFromUci(result.best)
assert(move, "Position changed before the move could be played")
local from, to = Engine.moveSquares64(move)
-- This is the game's normal board-move handler, including promotion/castling.
-- submitMove/playMove expose a separate raw route; they are deliberately unused.
local accepted = App.onBoardMove(from, to, Engine.movePromo(move), false)
assert(accepted == true, "The game's board handler rejected the move")
api.LastMove = result.best
api.LastError = nil
api.MovesPlayed += 1
oneMove = false
api.Status = if api.Enabled then "Move played; waiting for the board" else "Paused"
log("Played " .. result.best .. " (engine depth " .. tostring(result.depth or "?") .. ")")
end
local function buildGui()
local source = game:HttpGet("https://github.com/dawid-scripts/Fluent/releases/latest/download/main.lua")
local chunk, compileError = loadstring(source, "Fluent")
assert(chunk, "Could not load Fluent: " .. tostring(compileError))
local fluent = chunk()
assert(type(fluent) == "table", "Fluent did not load correctly")
guiLibrary = fluent
fluent.GUI.Name = "RoChessAutoFluent"
fluent.GUI.ResetOnSpawn = false
fluent.GUI.DisplayOrder = 10000
fluent.GUI.Parent = game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui")
local window = fluent:CreateWindow({
Title = "RoChess Auto",
SubTitle = "Vortex chess engine",
TabWidth = 120,
Size = UDim2.fromOffset(550, 470),
Acrylic = true,
Theme = "Dark",
MinimizeKey = Enum.KeyCode.RightControl,
})
local play = window:AddTab({Title = "Play", Icon = "play"})
local settings = window:AddTab({Title = "Settings", Icon = "settings"})
guiStatus = play:AddParagraph({Title = "Live status", Content = "Paused"})
guiToggle = play:AddToggle("RoChessAutoPlay", {
Title = "Auto-play",
Description = "Play the engine's best move on your turn.",
Default = api.Enabled,
Callback = function(value)
if not syncingGui and value ~= api.Enabled then api.SetEnabled(value) end
end,
})
play:AddButton({
Title = "Play one move",
Description = "Play once, then stay paused. Waits for your turn.",
Callback = api.PlayOnce,
})
guiMove = play:AddParagraph({Title = "Last move", Content = "No move played yet."})
guiError = play:AddParagraph({Title = "Messages", Content = "Ready"})
play:AddButton({
Title = "Stop and close",
Description = "Stop the engine and remove this panel.",
Callback = api.Stop,
})
local thinkSlider = settings:AddSlider("RoChessThinkTime", {
Title = "Thinking time (seconds)",
Description = "More time allows a deeper search. Reduced automatically when your clock is low.",
Default = SETTINGS.ThinkSeconds,
Min = 0.1,
Max = 3,
Rounding = 1,
Callback = function(value) SETTINGS.ThinkSeconds = value end,
})
settings:AddToggle("RoChessVortex", {
Title = "Use Vortex engine",
Description = "Use Vortex when available; otherwise use local analysis.",
Default = SETTINGS.UseServerEngine,
Callback = function(value) SETTINGS.UseServerEngine = value end,
})
settings:AddToggle("RoChessBlur", {
Title = "Acrylic blur",
Default = true,
Callback = function(value) fluent:ToggleAcrylic(value) end,
})
settings:AddParagraph({
Title = "Keyboard shortcuts",
Content = "F6: Pause / resume\nF7: Play one move\nF8: Stop and close\nRight Ctrl: Show / hide panel",
})
api.UI = {Library = fluent, Window = window, AutoPlay = guiToggle, ThinkTime = thinkSlider}
window:SelectTab(1)
end
local guiOk, guiFailure = pcall(buildGui)
if not guiOk then
api.LastError = "Could not create the Fluent panel: " .. tostring(guiFailure)
api.Stop()
error(api.LastError, 0)
end
table.insert(connections, Input.InputBegan:Connect(function(input, processed)
if processed or Input:GetFocusedTextBox() then return end
if input.KeyCode == Enum.KeyCode.F6 then
api.SetEnabled(not api.Enabled)
elseif input.KeyCode == Enum.KeyCode.F7 then
api.PlayOnce()
elseif input.KeyCode == Enum.KeyCode.F8 then
api.Stop()
end
end))
-- Real's reload lifecycle plus the global Stop() above prevent stacked copies.
local reloadState = getfenv().STATE
if type(reloadState) == "table" and type(reloadState.onCleanup) == "function" then
reloadState.onCleanup(api.Stop)
end
-- Status refresh runs separately so the panel keeps updating during a search.
task.spawn(function()
while api.Running do
if guiLibrary and guiLibrary.Unloaded then
api.Stop()
break
end
local ok, err = pcall(refreshGui)
if not ok then
warn("[RoChessAuto] Panel update failed: " .. tostring(err))
api.Stop()
break
end
task.wait(0.25)
end
end)
task.spawn(function()
while api.Running do
api.Busy = true
local ok, err = pcall(step)
api.Busy = false
if not ok then
api.LastError = tostring(err)
api.Status = "Error: " .. api.LastError
warn("[RoChessAuto] " .. api.LastError)
task.wait(2)
end
task.wait(math.clamp(tonumber(SETTINGS.PollSeconds) or 0.2, 0.1, 2))
end
end)
api.Status = if api.Enabled then "Waiting for a game" else "Paused"
refreshGui()
log("Fluent panel ready. Right Ctrl = show/hide | F6 = pause/resume | F7 = one move | F8 = stop")Review the source before using it. Executions count successful copies, capped at 10 per user and network per script in 24 hours.
Community discussion
0Start the conversation.


Your community, in one place