feat: extend warren with splitCommaList and playLoop
This commit is contained in:
1 parent
2dbaa30813
commit
15e1cbd8a5
1 file changed
+141
-1
+141
-1
@@ -1,12 +1,44 @@
|
||||
-- Headless entry point. Examples:
|
||||
-- lua warren.lua --seed 42 --auto 50
|
||||
-- lua warren.lua --seed 7 --auto 30 --save run.sav
|
||||
-- lua warren.lua --seed 3 --class ranger --auto 50
|
||||
-- lua warren.lua --load run.sav
|
||||
-- lua warren.lua --meta legacy.sav --seed 3 --auto 200
|
||||
-- lua warren.lua --meta legacy.sav --buy vigor
|
||||
-- lua warren.lua --meta legacy.sav --report
|
||||
-- lua warren.lua --meta legacy.sav --seed 3 --auto 200 --mutators famine,nightblind
|
||||
-- lua warren.lua --seed 3 --auto 200 --morgue run.morgue
|
||||
-- lua warren.lua --seed 3 --auto 200 --verify
|
||||
-- lua warren.lua --challenge 42 --auto 200
|
||||
-- lua warren.lua --meta legacy.sav --export-csv runs.csv
|
||||
-- lua warren.lua --meta legacy.sav --export-compendium bestiary.csv
|
||||
-- lua warren.lua --seed 3 --class fighter --play
|
||||
-- lua warren.lua --load run.sav --play
|
||||
|
||||
local scriptDir = arg[0]:match("^(.*)[/\\]") or "."
|
||||
package.path = scriptDir .. "/src/?.lua;" .. scriptDir .. "/../src/?.lua;" .. package.path
|
||||
|
||||
local Game = require("game")
|
||||
local Meta = require("meta")
|
||||
local Compendium = require("compendium")
|
||||
local Graves = require("graves")
|
||||
local Ascension = require("ascension")
|
||||
local Morgue = require("morgue")
|
||||
local Leaderboard = require("leaderboard")
|
||||
local Replay = require("replay")
|
||||
local Challenges = require("challenges")
|
||||
local Analytics = require("analytics")
|
||||
local Titles = require("titles")
|
||||
local Export = require("export")
|
||||
local Streaks = require("streaks")
|
||||
|
||||
local function splitCommaList(text)
|
||||
local list = {}
|
||||
for part in text:gmatch("[^,]+") do
|
||||
list[#list + 1] = part
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local function parseArgs(argv)
|
||||
local opts = { seed = os.time() & 0xFFFF, auto = 0 }
|
||||
@@ -17,13 +49,121 @@ local function parseArgs(argv)
|
||||
elseif flag == "--auto" then opts.auto = math.tointeger(tonumber(argv[i + 1])); i = i + 2
|
||||
elseif flag == "--save" then opts.save = argv[i + 1]; i = i + 2
|
||||
elseif flag == "--load" then opts.load = argv[i + 1]; i = i + 2
|
||||
elseif flag == "--class" then opts.class = argv[i + 1]; i = i + 2
|
||||
elseif flag == "--meta" then opts.metaPath = argv[i + 1]; i = i + 2
|
||||
elseif flag == "--buy" then opts.buy = argv[i + 1]; i = i + 2
|
||||
elseif flag == "--report" then opts.report = true; i = i + 1
|
||||
elseif flag == "--morgue" then opts.morguePath = argv[i + 1]; i = i + 2
|
||||
elseif flag == "--verify" then opts.verify = true; i = i + 1
|
||||
elseif flag == "--mutators" then opts.mutators = splitCommaList(argv[i + 1]); i = i + 2
|
||||
elseif flag == "--challenge" then opts.challengeDay = math.tointeger(tonumber(argv[i + 1])); i = i + 2
|
||||
elseif flag == "--export-csv" then opts.exportPath = argv[i + 1]; i = i + 2
|
||||
elseif flag == "--export-compendium" then opts.exportCompendiumPath = argv[i + 1]; i = i + 2
|
||||
elseif flag == "--play" then opts.play = true; i = i + 1
|
||||
else i = i + 1 end
|
||||
end
|
||||
return opts
|
||||
end
|
||||
|
||||
-- Interactive terminal loop: renders the current frame, reads one line of
|
||||
-- input, and hands it straight to Game:manualStep - the same command
|
||||
-- interpreter (see commands.lua) a scripted test drives without a real
|
||||
-- terminal attached. "quit"/"exit"/"q" leave the run exactly where it
|
||||
-- stands rather than ending it, so a save taken right after --play still
|
||||
-- captures whatever the last accepted command left behind. Runs until the
|
||||
-- player dies, wins, quits, or stdin closes (a redirected script of
|
||||
-- commands hitting EOF ends the loop the same way an interactive quit
|
||||
-- would).
|
||||
local function playLoop(game)
|
||||
io.write(game:render(), "\n")
|
||||
while game.player:isAlive() and not game.won do
|
||||
io.write("> ")
|
||||
io.flush()
|
||||
local line = io.read("l")
|
||||
if not line then break end
|
||||
local command = line:match("^%s*(.-)%s*$")
|
||||
if command == "quit" or command == "exit" or command == "q" then
|
||||
break
|
||||
elseif command ~= "" then
|
||||
local _, message = game:manualStep(command)
|
||||
if message then io.write(message, "\n") end
|
||||
io.write(game:render(), "\n")
|
||||
end
|
||||
end
|
||||
if game.won then
|
||||
io.write("you escape with the Amulet of Warren - you win!\n")
|
||||
elseif not game.player:isAlive() then
|
||||
io.write("you have died.\n")
|
||||
end
|
||||
end
|
||||
|
||||
local function reportMeta(meta)
|
||||
local toNext = Titles.toNext(meta.renown)
|
||||
local titleLine = toNext and string.format("%s (%d renown to next title)", Titles.forRenown(meta.renown), toNext)
|
||||
or string.format("%s (top of the ladder)", Titles.forRenown(meta.renown))
|
||||
io.write(string.format("renown %d | runs %d | wins %d | best depth %d | win rate %.0f%% | title %s\n",
|
||||
meta.renown, meta.totalRuns, meta.wins, meta.bestDepth, Analytics.winRate(meta) * 100, titleLine))
|
||||
local streakLength, streakOutcome = Streaks.current(meta.runHistory)
|
||||
io.write(string.format("streak: %d %s in a row | best win streak %d\n",
|
||||
streakLength, streakOutcome == nil and "runs" or (streakOutcome and "wins" or "losses"),
|
||||
Streaks.bestWinStreak(meta.runHistory)))
|
||||
for _, def in ipairs(Meta.PERKS) do
|
||||
io.write(string.format(" perk %-12s %s (%d renown)\n",
|
||||
def.key, Meta.isOwned(meta, def.key) and "[owned]" or "[locked]", def.cost))
|
||||
end
|
||||
io.write("challenges:\n")
|
||||
for _, preset in ipairs(Challenges.CATALOG) do
|
||||
io.write(" " .. Challenges.describe(preset) .. "\n")
|
||||
end
|
||||
for _, ach in ipairs(Meta.ACHIEVEMENTS) do
|
||||
io.write(string.format(" achievement %-14s %s\n",
|
||||
ach.key, meta.achievements[ach.key] and "[unlocked]" or "[locked]"))
|
||||
end
|
||||
for _, def in ipairs(Ascension.MUTATORS) do
|
||||
io.write(string.format(" mutator %-12s %s (+%d%% renown) %s\n",
|
||||
def.key, def.desc, def.renownBonus, Ascension.isUnlocked(meta, def.key) and "[unlocked]" or "[locked]"))
|
||||
end
|
||||
io.write("compendium:\n")
|
||||
for _, entry in ipairs(Compendium.entries(meta.compendium or Compendium.new())) do
|
||||
if entry.seen then
|
||||
io.write(string.format(" %-14s slain %-3d %s\n", entry.name, entry.kills, entry.flavor or ""))
|
||||
else
|
||||
io.write(string.format(" %-14s [undiscovered]\n", entry.key))
|
||||
end
|
||||
end
|
||||
local board = meta.leaderboard or Leaderboard.new()
|
||||
io.write("leaderboard:\n")
|
||||
for rank, entry in ipairs(Leaderboard.top(board)) do
|
||||
io.write(string.format(" #%-2d %-6d %-10s depth %-2d turn %-5d seed %s %s\n",
|
||||
rank, entry.score, entry.className, entry.depth, entry.turn, tostring(entry.seed),
|
||||
entry.won and "(won)" or ""))
|
||||
end
|
||||
if #board > 0 then
|
||||
io.write(string.format(" average score %.1f | favorite class %s\n",
|
||||
Analytics.averageScore(board), Analytics.favoriteClass(board) or "none"))
|
||||
end
|
||||
end
|
||||
|
||||
local opts = parseArgs(arg)
|
||||
local game = opts.load and Game.load(opts.load) or Game.new(opts.seed)
|
||||
|
||||
local challengePreset
|
||||
if opts.challengeDay then
|
||||
challengePreset, opts.seed = Challenges.forDay(opts.challengeDay)
|
||||
opts.class = challengePreset.class
|
||||
opts.mutators = challengePreset.mutators
|
||||
io.write(string.format("challenge: %s, seed %d\n", Challenges.describe(challengePreset), opts.seed))
|
||||
end
|
||||
|
||||
local meta = opts.metaPath and Meta.load(opts.metaPath) or nil
|
||||
|
||||
if meta and opts.buy then
|
||||
local success, message = Meta.purchase(meta, opts.buy)
|
||||
io.write(message, "\n")
|
||||
if not success then os.exit(1) end
|
||||
end
|
||||
|
||||
local game = opts.load and Game.load(opts.load)
|
||||
or Game.new(opts.seed, { class = opts.class, meta = meta, graves = meta and meta.graves, mutators = opts.mutators })
|
||||
|
||||
for _ = 1, opts.auto do
|
||||
if not game.player:isAlive() then break end
|
||||
|
||||
Reference in new issue
Block a user