📖 Scripting Guide

Your bot is a Lua script that returns "rock", "paper", or "scissors". Here's everything you need to write a winning strategy.

🎯 The Basics

Your script receives a game table and must return a valid move:

-- Simplest possible bot: always plays rock
return "rock"

That's it! But to win, you'll want to use the game context:

-- Use the round number to mix it up
if game.round % 3 == 1 then
  return "rock"
elseif game.round % 3 == 2 then
  return "paper"
else
  return "scissors"
end

🎮 The game Table

Your script has access to these variables:

FieldTypeDescription
game.roundnumberCurrent round (1–100)
game.my_historytableYour previous moves (strings)
game.their_historytableOpponent's previous moves (strings)
-- Counter the opponent's last move
if game.round == 1 then
  return "rock"
end

local last = game.their_history[#game.their_history]
if last == "rock" then return "paper"
elseif last == "scissors" then return "rock"
else return "scissors"
end

🧰 Lua Cheatsheet

Variables

local x = 10          -- number
local name = "hello"  -- string
local flag = true     -- boolean
local list = {1, 2, 3}  -- table (array)

Conditionals

if x > 5 then
  -- do something
elseif x == 3 then
  -- another thing
else
  -- fallback
end

Loops

-- Count loop
for i = 1, 10 do
  print(i)
end

-- Loop over a table
for i, value in ipairs(list) do
  print(i, value)
end

Tables (arrays & maps)

local t = {"rock", "paper", "scissors"}
print(#t)       -- length: 3
print(t[1])     -- "rock" (1-indexed!)

-- Key-value map
local scores = {rock = 0, paper = 0}
scores.rock = scores.rock + 1

Functions

local function counter(move)
  if move == "rock" then return "paper"
  elseif move == "scissors" then return "rock"
  else return "scissors"
  end
end

return counter(game.their_history[#game.their_history] or "rock")

Math & Random

math.random()       -- float 0.0–1.0
math.random(3)      -- integer 1, 2, or 3
math.max(1, 5, 3)   -- 5
math.min(1, 5, 3)   -- 1
math.floor(3.7)     -- 3

String Operations

string.len("hello")         -- 5
string.sub("hello", 1, 3)   -- "hel"
"rock" == "rock"            -- true

💡 Strategy Ideas

  • Random: Pick a random move each round — hard to predict!
  • Counter: Beat whatever they played last round
  • Pattern detection: Track their move frequencies and counter the most common
  • Mixed strategy: Play randomly but weight towards countering their favorite move
  • Markov chain: Predict their next move based on transitions between their moves
-- Frequency counter: play the counter to their most common move
local counts = {rock = 0, paper = 0, scissors = 0}
for _, move in ipairs(game.their_history) do
  counts[move] = counts[move] + 1
end

local most_common = "rock"
for move, count in pairs(counts) do
  if count > counts[most_common] then
    most_common = move
  end
end

-- Counter the most common
if most_common == "rock" then return "paper"
elseif most_common == "scissors" then return "rock"
else return "scissors"
end

⚠️ Rules & Limits

  • Scripts must return "rock", "paper", or "scissors"
  • Max 100,000 instructions per round (no infinite loops)
  • Max 10KB script size
  • No file/network/OS access — sandbox only
  • Available globals: math, string, table, pairs, ipairs, type, tostring, tonumber, select, unpack, pcall
  • If your script errors or returns an invalid move, you forfeit that round

🐛 Reading Error Messages

When your script has a bug, the test runner shows an error like:

💥 line 5: syntax error near 'if'

What the parts mean

PartMeaning
line 5The line number in your script where the error was found
syntax error near 'if'What went wrong — Lua found something unexpected near if

Common errors

ErrorCauseFix
syntax error near 'X'Unexpected token — usually a missing then, end, or typoCheck the line above for missing keywords
'end' expectedAn if, for, or function block wasn't closedAdd the missing end
attempt to index a nil valueAccessing a field on something that doesn't existCheck variable names and that tables are populated
attempt to call a nil valueCalling a function that doesn't existCheck spelling; some globals are removed in the sandbox
Script timeout: exceeded maximum instructionsInfinite loop or very long computationAdd a loop exit condition or simplify logic

Tips

  • Errors often point to the line after the actual mistake — check the line above too
  • Use the Test button frequently as you write — catch errors early
  • If your script returns an invalid move (not rock/paper/scissors), you'll see a ⚠️ warning instead of an error

🔗 Learn More

Script Wars — write bots, win glory