Add testBall.lua, testMain.lua, and testStatistics.lua
testMain.lua is really just a does-this-big-harness-work check right now, but it does work! Extract statistics.lua for testing Consolidate BoxScore ALL into draw/box-score.lua
This commit is contained in:
parent
48a9854653
commit
b44756ff57
|
@ -85,3 +85,8 @@ function Ball:launch(destX, destY, easingFunc, flyTimeMs, floaty, customBallScal
|
||||||
self.floatAnimator:reset(flyTimeMs)
|
self.floatAnimator:reset(flyTimeMs)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
if not playdate or playdate.TEST_MODE then
|
||||||
|
return Ball
|
||||||
|
end
|
||||||
|
|
|
@ -1,61 +1,3 @@
|
||||||
---@alias TeamInningData { score: number, pitching: { balls: number, strikes: number }, hits: XyPair[] }
|
|
||||||
|
|
||||||
--- E.g. statistics[1].home.pitching.balls
|
|
||||||
---@class Statistics
|
|
||||||
---@field innings: (table<TeamId, TeamInningData>)[]
|
|
||||||
|
|
||||||
Statistics = {}
|
|
||||||
|
|
||||||
local function newTeamInning()
|
|
||||||
return {
|
|
||||||
score = 0,
|
|
||||||
pitching = {
|
|
||||||
balls = 0,
|
|
||||||
strikes = 0,
|
|
||||||
},
|
|
||||||
hits = {},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
---@return table<TeamId, TeamInningData>
|
|
||||||
local function newInning()
|
|
||||||
return {
|
|
||||||
home = newTeamInning(),
|
|
||||||
away = newTeamInning(),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
---@return Statistics
|
|
||||||
function Statistics.new()
|
|
||||||
return setmetatable({
|
|
||||||
innings = { newInning() },
|
|
||||||
}, { __index = Statistics })
|
|
||||||
end
|
|
||||||
|
|
||||||
function Statistics:pushInning()
|
|
||||||
self.innings[#self.innings + 1] = newInning()
|
|
||||||
end
|
|
||||||
|
|
||||||
---@class BoxScore
|
|
||||||
---@field stats Statistics
|
|
||||||
---@field private targetY number
|
|
||||||
BoxScore = {}
|
|
||||||
|
|
||||||
---@param stats Statistics
|
|
||||||
function BoxScore.new(stats)
|
|
||||||
return setmetatable({
|
|
||||||
stats = stats,
|
|
||||||
targetY = 0,
|
|
||||||
}, { __index = BoxScore })
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO? Some other stats
|
|
||||||
-- * Scroll left and right through games that go into extra innings
|
|
||||||
-- * Scroll up and down through other stats.
|
|
||||||
-- + Balls and strikes
|
|
||||||
-- + Batting average
|
|
||||||
-- + Farthest hit ball
|
|
||||||
|
|
||||||
local MarginY <const> = 70
|
local MarginY <const> = 70
|
||||||
|
|
||||||
local SmallFont <const> = playdate.graphics.font.new("fonts/font-full-circle.pft")
|
local SmallFont <const> = playdate.graphics.font.new("fonts/font-full-circle.pft")
|
||||||
|
@ -101,6 +43,20 @@ local function drawInning(x, inningNumber, homeScore, awayScore)
|
||||||
ScoreFont:drawTextAligned(homeScore, x, AwayY, gfx.kAlignRight)
|
ScoreFont:drawTextAligned(homeScore, x, AwayY, gfx.kAlignRight)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@class BoxScore
|
||||||
|
---@field stats Statistics
|
||||||
|
---@field private targetY number
|
||||||
|
BoxScore = {}
|
||||||
|
|
||||||
|
---@param stats Statistics
|
||||||
|
---@return BoxScore
|
||||||
|
function BoxScore.new(stats)
|
||||||
|
return setmetatable({
|
||||||
|
stats = stats,
|
||||||
|
targetY = 0,
|
||||||
|
}, { __index = BoxScore })
|
||||||
|
end
|
||||||
|
|
||||||
function BoxScore:drawBoxScore()
|
function BoxScore:drawBoxScore()
|
||||||
local inningStart = 4 + (AwayWidth * 1.5)
|
local inningStart = 4 + (AwayWidth * 1.5)
|
||||||
local widthAndMarg = InningDrawWidth + 4
|
local widthAndMarg = InningDrawWidth + 4
|
||||||
|
|
13
src/main.lua
13
src/main.lua
|
@ -38,6 +38,7 @@ import 'fielding.lua'
|
||||||
import 'graphics.lua'
|
import 'graphics.lua'
|
||||||
import 'npc.lua'
|
import 'npc.lua'
|
||||||
import 'pitching.lua'
|
import 'pitching.lua'
|
||||||
|
import 'statistics.lua'
|
||||||
|
|
||||||
import 'draw/box-score.lua'
|
import 'draw/box-score.lua'
|
||||||
import 'draw/fans.lua'
|
import 'draw/fans.lua'
|
||||||
|
@ -242,13 +243,10 @@ function Game:pitcherIsReady()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Game:checkForGameOver()
|
function Game:checkForGameOver()
|
||||||
local homeScore, awayScore = utils.totalScores(self.state.stats)
|
|
||||||
local isFinalInning = self.state.inning >= self.settings.finalInning
|
|
||||||
local gameOver = isFinalInning and self.state.battingTeam == "home" and awayScore ~= homeScore
|
|
||||||
gameOver = gameOver or self.state.battingTeam == "away" and isFinalInning and homeScore > awayScore
|
|
||||||
Fielding.celebrate()
|
Fielding.celebrate()
|
||||||
|
|
||||||
if gameOver then
|
local state = self.state
|
||||||
|
if state.stats:gameIsOver(state.inning, self.settings.finalInning, state.battingTeam) then
|
||||||
self.announcer:say("THAT'S THE BALL GAME!")
|
self.announcer:say("THAT'S THE BALL GAME!")
|
||||||
playdate.timer.new(3000, function()
|
playdate.timer.new(3000, function()
|
||||||
transitionTo(BoxScore.new(self.state.stats))
|
transitionTo(BoxScore.new(self.state.stats))
|
||||||
|
@ -727,6 +725,11 @@ function Game:update()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
if not playdate or playdate.TEST_MODE then
|
||||||
|
return Game
|
||||||
|
end
|
||||||
|
|
||||||
playdate.display.setRefreshRate(50)
|
playdate.display.setRefreshRate(50)
|
||||||
gfx.setBackgroundColor(gfx.kColorWhite)
|
gfx.setBackgroundColor(gfx.kColorWhite)
|
||||||
playdate.setMenuImage(gfx.image.new("images/game/menu-image.png"))
|
playdate.setMenuImage(gfx.image.new("images/game/menu-image.png"))
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
-- TODO? Some other stats
|
||||||
|
-- * Scroll left and right through games that go into extra innings
|
||||||
|
-- * Scroll up and down through other stats.
|
||||||
|
-- + Balls and strikes
|
||||||
|
-- + Batting average
|
||||||
|
-- + Farthest hit ball
|
||||||
|
|
||||||
|
local function newTeamInning()
|
||||||
|
return {
|
||||||
|
score = 0,
|
||||||
|
pitching = {
|
||||||
|
balls = 0,
|
||||||
|
strikes = 0,
|
||||||
|
},
|
||||||
|
hits = {},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return table<TeamId, TeamInningData>
|
||||||
|
local function newInning()
|
||||||
|
return {
|
||||||
|
home = newTeamInning(),
|
||||||
|
away = newTeamInning(),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@alias TeamInningData { score: number, pitching: { balls: number, strikes: number }, hits: XyPair[] }
|
||||||
|
|
||||||
|
--- E.g. statistics[1].home.pitching.balls
|
||||||
|
---@class Statistics
|
||||||
|
---@field innings (table<TeamId, TeamInningData>)[]
|
||||||
|
Statistics = {}
|
||||||
|
|
||||||
|
---@return Statistics
|
||||||
|
function Statistics.new()
|
||||||
|
return setmetatable({
|
||||||
|
innings = { newInning() },
|
||||||
|
}, { __index = Statistics })
|
||||||
|
end
|
||||||
|
|
||||||
|
function Statistics:pushInning()
|
||||||
|
self.innings[#self.innings + 1] = newInning()
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param inning number
|
||||||
|
---@param finalInning number
|
||||||
|
---@param battingTeam TeamId
|
||||||
|
---@return boolean gameOver
|
||||||
|
function Statistics:gameIsOver(inning, finalInning, battingTeam)
|
||||||
|
local homeScore, awayScore = utils.totalScores(self)
|
||||||
|
local isFinalInning = inning >= finalInning
|
||||||
|
local gameOver = isFinalInning and battingTeam == "home" and awayScore ~= homeScore
|
||||||
|
gameOver = gameOver or battingTeam == "away" and isFinalInning and homeScore > awayScore
|
||||||
|
return gameOver
|
||||||
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore
|
||||||
|
if not playdate or playdate.TEST_MODE then
|
||||||
|
return Statistics
|
||||||
|
end
|
|
@ -2,7 +2,9 @@ utils = require("utils")
|
||||||
|
|
||||||
local currentTimeMs = 0
|
local currentTimeMs = 0
|
||||||
|
|
||||||
local mockPlaydate = {
|
local mockPlaydate = {}
|
||||||
|
|
||||||
|
mockPlaydate = {
|
||||||
TEST_MODE = true,
|
TEST_MODE = true,
|
||||||
skipMs = function(skip)
|
skipMs = function(skip)
|
||||||
currentTimeMs = currentTimeMs + skip
|
currentTimeMs = currentTimeMs + skip
|
||||||
|
@ -11,13 +13,21 @@ local mockPlaydate = {
|
||||||
currentTimeMs = currentTimeMs + 1
|
currentTimeMs = currentTimeMs + 1
|
||||||
return currentTimeMs
|
return currentTimeMs
|
||||||
end,
|
end,
|
||||||
|
easingFunctions = {},
|
||||||
timer = {
|
timer = {
|
||||||
|
lastTimer = {
|
||||||
|
mockCompletion = function()
|
||||||
|
error("No lastTimer set!")
|
||||||
|
end,
|
||||||
|
},
|
||||||
new = function(_, callback)
|
new = function(_, callback)
|
||||||
return {
|
local timer = {
|
||||||
mockCompletion = function()
|
mockCompletion = function()
|
||||||
callback()
|
callback()
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
mockPlaydate.timer.lastTimer = timer
|
||||||
|
return timer
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
graphics = {
|
graphics = {
|
||||||
|
@ -38,6 +48,21 @@ local mockPlaydate = {
|
||||||
return {}
|
return {}
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
image = {
|
||||||
|
new = function()
|
||||||
|
return {}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sound = {
|
||||||
|
sampleplayer = {
|
||||||
|
new = function()
|
||||||
|
return {
|
||||||
|
play = function() end,
|
||||||
|
setFinishCallback = function() end,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
require("test/setup")
|
||||||
|
|
||||||
|
local Ball = require("ball")
|
||||||
|
|
||||||
|
function testMarkUncatchable()
|
||||||
|
local ball = Ball.new(playdate.graphics.animator)
|
||||||
|
luaunit.assertIsTrue(ball.catchable, "Ball should start catchable")
|
||||||
|
ball:markUncatchable()
|
||||||
|
luaunit.assertIsFalse(ball.catchable, "Ball should not be catchable immediately after mark")
|
||||||
|
playdate.timer.lastTimer.mockCompletion()
|
||||||
|
luaunit.assertIsTrue(ball.catchable, "Ball should return to catchability after its timer expires")
|
||||||
|
end
|
||||||
|
|
||||||
|
os.exit(luaunit.LuaUnit.run())
|
|
@ -0,0 +1,28 @@
|
||||||
|
require("test/setup")
|
||||||
|
require("draw/panner")
|
||||||
|
|
||||||
|
function string.starts(str, start)
|
||||||
|
return string.sub(str, 1, str.len(start)) == start
|
||||||
|
end
|
||||||
|
|
||||||
|
import = function(target)
|
||||||
|
if string.starts(target, "CoreLibs") or string.starts(target, "draw/") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- Remove .lua
|
||||||
|
require(target:sub(1, #target - 4))
|
||||||
|
end
|
||||||
|
|
||||||
|
local Game = require("main")
|
||||||
|
|
||||||
|
local settings = {
|
||||||
|
homeTeamSpriteGroup = {},
|
||||||
|
awayTeamSpriteGroup = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
function testStandaloneInit()
|
||||||
|
-- Harness should be fleshed-out enough to init without error.
|
||||||
|
Game.new(settings, announcer)
|
||||||
|
end
|
||||||
|
|
||||||
|
os.exit(luaunit.LuaUnit.run())
|
|
@ -0,0 +1,19 @@
|
||||||
|
require("test/setup")
|
||||||
|
|
||||||
|
local Statistics = require("statistics")
|
||||||
|
|
||||||
|
function testReportGameOver()
|
||||||
|
---@type Statistics
|
||||||
|
local stats = Statistics.new()
|
||||||
|
stats.innings[1].home.score = 0
|
||||||
|
stats.innings[1].away.score = 0
|
||||||
|
luaunit.assertIsFalse(stats:gameIsOver(9, 9, "home"), "Tie games should not report a game over")
|
||||||
|
|
||||||
|
stats.innings[1].home.score = 1
|
||||||
|
luaunit.assertIsTrue(stats:gameIsOver(9, 9, "home"), "Team in lead should report a game over")
|
||||||
|
|
||||||
|
stats.innings[1].home.score = 1
|
||||||
|
luaunit.assertIsFalse(stats:gameIsOver(1, 9, "home"), "Should not game over with innings left")
|
||||||
|
end
|
||||||
|
|
||||||
|
os.exit(luaunit.LuaUnit.run())
|
Loading…
Reference in New Issue