I thought I knew what we had to do today but I was a bit mistaken. We need more, and better thinking … and code.

There was an embarrassing defect in the last release. Attempts to fire a missile accessed an out of date notion, `Ship.ang`, resulting in an error and no missiles. So one mission this morning is to think about how that happened and what we can do about it.

It’s easy – and true – to say “it was just an oversight”. Certainly it was. But how do we fix our tendency to miss things from time to time? “Think better” isn’t very good advice: we’re thinking as best we can. So we’ll see if there are ways to program that allow for fewer oversights.

I’m sure some of you are thinking about strict typing right now. In a more strict language, the fact that `ang` wasn’t defined would have given us an error. We’ll talk about that when we get to the topic.

What I plan to do this morning is a further improvement to the Missile. When a missile is fired, its velocity is presently speed 2.0 in the direction the ship is pointing. That’s not quite right: the missile should get a velocity equal to its own firing velocity *plus the velocity of the ship”. So let’s take a look at that:

``````function Missile:init(ship)
function die()
self:die()
end
self.pos = ship.pos
Missiles[self] = self
tween(3, self, {}, tween.easing.linear, die)
end
``````

The `self.vel` line there is the one I had to fix last night, by changing the use of `ang`, which no longer exists, to `radians`. We need to add the velocity of the ship to that value. Conveniently, the ship knows its velocity, so we can do this:

``````    self.vel = vec2(MissileVelocity,0):rotate(ship.radians) + ship.velocity
``````

That works as intended. Now that that little task is done, let’s commit “missiles track” and look upon our works and despair (just a little).

## That Darn Oversight

When we added acceleration to the Ship, I made a mistake between degrees and radians. Codea doesn’t help much with this, not least because sometimes it uses degrees and sometimes radians. So we changed the member variable from `ang` to `radians`, hoping that would help us remember the data type.

The firing problem was that Missile, which looks to the Ship during creation, was referring to `ang` and I didn’t catch it. My question now is whether there is something we can do beyond “be smarter”.

I have two ideas. First, we could have some tests that check whether things are being computed correctly. Certainly if there had been a test for missile velocity, it would have failed. It’s a bit late to close that barn door but we might want to write a test or two to improve our testing habits. As I’ve said before, in a visual app like this game, I have difficulty seeing what tests to write, and it’s so easy just to run the program and see if a change works.

Unless you check acceleration but don’t ever fire a missile after adding acceleration. It was breakfast time, so I didn’t do even a bit of game play.

Since you can’t expect me to skip a meal, it might be fair to ask me to improve the automated tests.

My second idea is to find ways to improve the code to make oversights less likely. The best way to do that is to make the code simpler, and the second best way is to make it more consistent.

(I don’t really know if those are the best ways in the world. They’re the best ones I can think of right now. See previous remarks about being smarter.)

We have three moving objects just now, asteroids, the ship, and the missiles. Their moving code looks like this:

``````
function moveAsteroid(asteroid)
local step = Ratio*vec2(Vel,0):rotate(asteroid.angle)
local pos = asteroid.pos + step
asteroid.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end

function actualShipMove()
if Button.go then
Ship.velocity = Ship.velocity + accel
Ship.velocity = maximize(Ship.velocity, 3)
end
Ship.pos = Ship.pos + Ship.velocity
Ship.pos = vec2(keepInBounds(Ship.pos.x, WIDTH), keepInBounds(Ship.pos.y, HEIGHT))
end

function Missile:move()
self.pos = self.pos + Ratio*self.vel
self.pos = vec2(keepInBounds(self.pos.x, WIDTH), keepInBounds(self.pos.y, HEIGHT))
end
``````

These are doing roughly the same thing, but they don’t look as similar as they might. Furthermore … where is `Ratio` being used in the ship at all? Remember that `Ratio` compensates for processor speed, to keep game speed the same on different processors. I don’t see that happening for the ship.

Another oversight,you say, Jeffries? Been a lot of oversights lately, wouldn’t you say? What’s going to be done about these oversights, hmm?

What we have here, lords and ladies, is duplication. Not your simple these two lines are the same kind of duplication, but the more pernicious these lines are doing the same things in different ways kind of duplication.

What do we do with duplication? We remove it. We do that “same thing” in one and only one place. How do we get there? One really good way is to make the duplication more visible, so that the parts that are the same look the same, and the parts that are different are set off from the parts that are the same.

## Common Elements

What do all our moving objects have in common?

1. They all have a position in the world. It’s even named `pos` in all cases.
2. They all clip their position to remain within screen boundaries.
3. They all add a small amount, the step, to their position, on every move.
4. The step should be adjusted by Ratio, to give them all the same speed across processors. (The ship seems not to do this at present.)
5. Probably, all velocities should be limited, not necessarily to the same limit.

Let’s look at what they don’t have in common:

1. The asteroids and missiles never change speed or direction: they just plod along at whatever pace they’re given.
2. The ship does change speed: it can accelerate in any direction.

One way of restating the difference is that the acceleration of asteroids and missiles is zero, and that of the ship may not be.

This is suggesting to me that everyone’s motion might look like this:

``````adjustVelocityByAcceleration() -- ship only
keepPositionInBounds() -- everyone
``````

Now that I look at that I think we can do better. I’ll leave that there because I did think it, and my job here is to show you how things really happen, not how some god of programming would do it.

Let’s go back to the things in common and work with them.

1. Everyone has a step, an increment to position;
2. Everyone adjusts that step by Ratio
3. Everyone adds the step to position
4. Everyone keeps the position in bounds.

There are other operations that are not shared, at least this: some people change the step, for some it is constant. If it’s constant, we should just use it, adjusting it not at all, for efficiency.

So, in principle, asteroids and missiles should know their `step`, which would be a vector embodying their velocity and direction, and including the application of Ratio. They just do steps 3 and 4, given step.

And the ship computes its step, adding in any acceleration, and limiting by maximum velocity, adjusting for Ratio … and then does steps 3 and 4.

And the missiles need to be sure to apply Ratio and maximum velocity (if they have it) when they set up their step.

And we need a shared name for the step: and it should be velocity.

## We have a plan

Well, we have enough of a plan. I’m moving toward each of these three objects having a move method that adjusts by velocity and clamps values to the screen, and to have them all be identical.

Let’s start to make that happen. First, Asteroid, which looks like this:

``````function createAsteroid()
local a = {}
a.pos = vec2(math.random(WIDTH), math.random(HEIGHT))
a.angle = math.random()*2*math.pi
a.shape = Rocks[math.random(1,4)]
a.scale = 16
return a
end

function moveAsteroid(asteroid)
local step = Ratio*vec2(Vel,0):rotate(asteroid.angle)
local pos = asteroid.pos + step
asteroid.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end

function keepInBounds(value, bound)
return (value+bound)%bound
end
``````

We want to compute `step` during creation. But wait, we can’t apply Ratio at creation time, it can vary during the run. So we don’t do just steps 3 and 4 from above, we do 2, 3, and 4. I’ll refactor move and create thus:

``````function moveAsteroid(asteroid)
local pos = asteroid.pos + Ratio*asteroid.step
asteroid.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end

function createAsteroid()
local a = {}
a.pos = vec2(math.random(WIDTH), math.random(HEIGHT))
a.shape = Rocks[math.random(1,4)]
a.scale = 16
local angle = math.random()*2*math.pi
a.step = Ratio*vec2(Vel,0):rotate(angle)
return a
end
``````

That works, by actual test. Commit “canonical asteroid motion”.

Now Missile, which looks like this:

``````function Missile:init(ship)
function die()
self:die()
end
self.pos = ship.pos
Missiles[self] = self
tween(3, self, {}, tween.easing.linear, die)
end

function Missile:move()
self.pos = self.pos + Ratio*self.vel
self.pos = vec2(keepInBounds(self.pos.x, WIDTH), keepInBounds(self.pos.y, HEIGHT))
end
``````

This seems problematical, since missiles are objects and asteroids are not, but I’m going to let that slide for now. I think it’ll sort out.

I’ll rewrite the move to look more like the asteroids one:

``````function Missile:move()
local pos = self.pos + Ratio*self.vel
self.pos = vec2(keepInBounds(self.pos.x, WIDTH), keepInBounds(self.pos.y, HEIGHT))
end
``````

Now in Missile we called it `vel` and in Asteroid it’s `step`. Let’s stick with `step`. I was thinking velocity but the idea here is that all the velocity calculations have been done and we just have a step to take. So I’ll modify Missile to have `step` instead of `vel`.

``````local MissileVelocity = 2.0

function Missile:init(ship)
function die()
self:die()
end
self.pos = ship.pos
Missiles[self] = self
tween(3, self, {}, tween.easing.linear, die)
end
``````

Oops:

``````function Missile:move()
local pos = self.pos + Ratio*self.step
self.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end
``````

I need a refactoring browser. Forgot to remove the `self.` inside the `keepInBounds`.

These two functions are the same. They’re so much the same that I think I could use one of them to do the work of the other

Just for fun:

``````function Missile:move()
--local pos = self.pos + Ratio*self.step
--self.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
moveAsteroid(self)
end
``````

That actually works. The moveAsteroid function doesn’t know it wasn’t given an asteroid, it just looks for and uses `step` and `pos`.

That’s evil, of course, and I’ll back that change out for now. Then commit: “canonical missile motion”.

Now let’s do the ship. We’re doing to have to be careful, because the missile is referring to `ship.velocity` and we plan to change that to `step`. Don’t let me forget that.

Here’s the ship now:

``````function createShip()
Ship.pos = vec2(WIDTH, HEIGHT)/2
Ship.velocity = vec2(0,0)
end

function moveShip()
if Button.fire then if not Ship.holdFire then fireMissile() end end
if not Button.fire then Ship.holdFire = false end
actualShipMove()
end

function actualShipMove()
if Button.go then
Ship.velocity = Ship.velocity + accel
Ship.velocity = maximize(Ship.velocity, 3)
end
Ship.pos = Ship.pos + Ship.velocity
Ship.pos = vec2(keepInBounds(Ship.pos.x, WIDTH), keepInBounds(Ship.pos.y, HEIGHT))
end
``````

I rather hate that I allowed that `moveShip` function to keep its name. Anyway we’re here for a different purpose. I’ll name my new little mover, oh, `finallyMove` I guess, and we’ll do like this:

``````function createShip()
Ship.pos = vec2(WIDTH, HEIGHT)/2
Ship.step = vec2(0,0)
end

function actualShipMove()
if Button.go then
Ship.step = Ship.step + accel
Ship.step = maximize(Ship.step, 3)
end
finallyMove(Ship)
end

function finallyMove(ship)
local pos = ship.pos + Ratio*ship.step
Ship.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end
``````

That works, and I remembered to fix the missile also. But did I remember to put `Ratio` into all three functions?

``````function finallyMove(ship)
local pos = ship.pos + Ratio*ship.step
Ship.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end

function moveAsteroid(asteroid)
local pos = asteroid.pos + Ratio*asteroid.step
asteroid.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end

function Missile:move()
local pos = self.pos + Ratio*self.step
self.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end
``````

I did in fact remember. Yay, me. Maybe I can be smarter after all.

Now all three of these moving objects use the same code to move. They all point to a different object / table when the function runs, but that’s just fine.

Now we can consolidate that function if we care to. We could put a new copy of that function in Main, move the keep there as well, and use the new function for all three cases.

I don’t like that. It’s semi-OK for the asteroids and ship to consolidate that function, but it doesn’t consolidate well for the object-oriented missile to call a global function. I’m not even really happy about calling the `keepInBounds` that way.

What’s another way of making three different things use the same function?

This could get tricky. First, let’s commit “canonical ship motion”.

## Consolidating movement

I was thinking of providing a general motion function, since now we’ve written it three times, and plugging it into each of our tables. But in talking about this on one of my Slacks just now, I got an idea I like better: a method on Universe that moves things.

We can think of it as moving things according to the laws of the universe. It could even apply speed limits if we wish. So I’ll add a method to Universe and use it.

Maybe we’ll like this, maybe we won’t. Either way is OK. Here it is for missiles:

``````function Universe:moveObject(anObject)
local pos = anObject.pos + Ratio*anObject.step
anObject.pos = vec2(keepInBounds(pos.x, WIDTH), keepInBounds(pos.y, HEIGHT))
end

function Missile:move()
U:moveObject(self)
end
``````

Now we can clearly do ship and asteroid the same way, replacing their calls to their own moves with calls to this one:

``````function moveAsteroid(asteroid)
U:moveObject(asteroid)
end

function finallyMove(ship)
U:moveObject(ship)
end
``````

This works just fine. The question is whether we like it.

The good news is we have completely removed that duplication, hopefully preventing any further mistakes like leaving out Ratio or referring to non-existent variables.

The bad news is, now the universe moves all the things, instead of the things moving themselves. If I could have my druthers, I druther have objects moving themselves … all in the same way.

Is there a way to avoid the duplication but not cause the Universe to move all the objects … and do we even mind that it does move them?

For now, I think we’ll leave it this way. However, I think we should move the `keepInBounds` function to the Universe as well, since it should only be used right here.

``````function Universe:keepInBounds(value, bound)
return (value+bound)%bound
end
``````

That breaks a unit test, which was counting on `keepInBounds` being global. Fixed:

``````        _:test("Bounds function", function()
_:expect(U:keepInBounds(100,1000)).is(100)
_:expect(U:keepInBounds(1000,1000)).is(0)
_:expect(U:keepInBounds(1001,1000)).is(1)
_:expect(U:keepInBounds(-1,1000)).is(999)
end)
``````

Commit “universe moves things”.

## Wider View

Let’s think about this change. Taken overall, it’s pretty global, ultimately affecting five tabs, Test, Ship, Asteroid, Missile, and Universe. But we did it in small steps and rarely broke anything.

The errors I made were, I believe, mostly failures to catch all the occurrences of something that needed to change because its name or access had changed, Failing to add in a “self” where needed, or referencing an old variable name.

Oversights, in other words. I hope that this centralization will prevent further such oversights in this one area, but there are plenty of other areas to be concerned about, like the approximately 500 lines of code that are not moving the object.

I think this is a bit better, a bit more like it “should” be, but I’m not certain. I’m not entirely comfortable having motion done “to” an object rather than “by” the object. It makes a kind of sense, but it seems that it reduces their autonomy.

I wouldn’t do this trick to save memory by removing the duplication. I did it because it ensures that everyone who should do the same thing does in fact do the same thing. It’s a bit odd but for now, I’ll pay the price of oddness for the improvement in consistency.

I look forward to your comments (via Twitter or other means) and hope to see you next time!

``````--# Main
-- Asteroids
-- RJ 20200511

Touches = {}
Ratio = 1.0 -- draw time scaling ratio
Score = 0

function setup()
U = Universe()
U:createAsteroids()
Score = 0
--displayMode(FULLSCREEN_NO_BUTTONS)
createButtons()
createShip()
end

function draw()
Ratio = DeltaTime/0.0083333
--displayMode(FULLSCREEN_NO_BUTTONS)
checkButtons()
pushStyle()
background(40, 40, 50)
U:draw()
drawButtons()
drawShip()
moveShip()
drawMissiles()
drawSplats()
drawScore()
popStyle()
U:findCollisions()
end

function drawScore()
local s= "000000"..tostring(Score)
s = string.sub(s,-5)
fontSize(100)
text(s, 200, HEIGHT-60)
end

function touched(touch)
if touch.state == ENDED or touch.state == CANCELLED then
Touches[touch.id] = nil
else
Touches[touch.id] = touch
end
end

--# TestAsteroids
-- TestAsteroids
-- RJ 20200511

function testAsteroids()
CodeaUnit.detailed = true

_:describe("Asteroids First Tests", function()

_:before(function()
-- Some setup
end)

_:after(function()
-- Some teardown
end)

_:test("Hookup", function()
_:expect( 2+1 ).is(3)
end)

_:test("Random", function()
local min = 100
local max = 0
for i = 0,1000 do
local rand = math.random()*2*math.pi
if rand < min then min = rand end
if rand > max then max = rand end
end
_:expect(min < 0.01).is(true)
_:expect(max > 6.2).is(true)
end)

_:test("Rotated Length", function()
for i = 0, 1000 do
local rand = math.random()*2*math.pi
local v = vec2(1.5,0):rotate(rand)
local d = v:len()
_:expect(d > 1.495).is(true)
_:expect(d < 1.505).is(true)
end
end)

_:test("Some rotates go down", function()
local v = vec2(1,0):rotate(angle)
local rvx = v.x*1000//1
local rvy = v.y*1000//1
_:expect(rvx).is(707)
_:expect(rvy).is(-708)
end)

_:test("Bounds function", function()
_:expect(U:keepInBounds(100,1000)).is(100)
_:expect(U:keepInBounds(1000,1000)).is(0)
_:expect(U:keepInBounds(1001,1000)).is(1)
_:expect(U:keepInBounds(-1,1000)).is(999)
end)

end)
end

--# Shapes
RR1 = {
vec4(0.000000, 2.000000, 2.000000, 4.000000),
vec4(2.000000, 4.000000, 4.000000, 2.000000),
vec4(4.000000, 2.000000, 3.000000, 0.000000),
vec4(3.000000, 0.000000, 4.000000, -2.000000),
vec4(4.000000, -2.000000, 1.000000, -4.000000),
vec4(1.000000, -4.000000, -2.000000, -4.000000),
vec4(-2.000000, -4.000000, -4.000000, -2.000000),
vec4(-4.000000, -2.000000, -4.000000, 2.000000),
vec4(-4.000000, 2.000000, -2.000000, 4.000000),
vec4(-2.000000, 4.000000, 0.000000, 2.000000)
}

RR2 = {
vec4(2.000000, 1.000000, 4.000000, 2.000000),
vec4(4.000000, 2.000000, 2.000000, 4.000000),
vec4(2.000000, 4.000000, 0.000000, 3.000000),
vec4(0.000000, 3.000000, -2.000000, 4.000000),
vec4(-2.000000, 4.000000, -4.000000, 2.000000),
vec4(-4.000000, 2.000000, -3.000000, 0.000000),
vec4(-3.000000, 0.000000, -4.000000, -2.000000),
vec4(-4.000000, -2.000000, -2.000000, -4.000000),
vec4(-2.000000, -4.000000, -1.000000, -3.000000),
vec4(-1.000000, -3.000000, 2.000000, -4.000000),
vec4(2.000000, -4.000000, 4.000000, -1.000000),
vec4(4.000000, -1.000000, 2.000000, 1.000000)
}

RR3 = {
vec4(-2.000000, 0.000000, -4.000000, -1.000000),
vec4(-4.000000, -1.000000, -2.000000, -4.000000),
vec4(-2.000000, -4.000000, 0.000000, -1.000000),
vec4(0.000000, -1.000000, 0.000000, -4.000000),
vec4(0.000000, -4.000000, 2.000000, -4.000000),
vec4(2.000000, -4.000000, 4.000000, -1.000000),
vec4(4.000000, -1.000000, 4.000000, 1.000000),
vec4(4.000000, 1.000000, 2.000000, 4.000000),
vec4(2.000000, 4.000000, -1.000000, 4.000000),
vec4(-1.000000, 4.000000, -4.000000, 1.000000),
vec4(-4.000000, 1.000000, -2.000000, 0.000000)
}

RR4 = {
vec4(1.000000, 0.000000, 4.000000, 1.000000),
vec4(4.000000, 1.000000, 4.000000, 2.000000),
vec4(4.000000, 2.000000, 1.000000, 4.000000),
vec4(1.000000, 4.000000, -2.000000, 4.000000),
vec4(-2.000000, 4.000000, -1.000000, 2.000000),
vec4(-1.000000, 2.000000, -4.000000, 2.000000),
vec4(-4.000000, 2.000000, -4.000000, -1.000000),
vec4(-4.000000, -1.000000, -2.000000, -4.000000),
vec4(-2.000000, -4.000000, 1.000000, -3.000000),
vec4(1.000000, -3.000000, 2.000000, -4.000000),
vec4(2.000000, -4.000000, 4.000000, -2.000000),
vec4(4.000000, -2.000000, 1.000000, 0.000000)
}

Rocks = {RR1,RR2,RR3,RR4}
--# Ship
-- Ship
-- RJ 20200520

local Ship = {}

function createShip()
Ship.pos = vec2(WIDTH, HEIGHT)/2
Ship.step = vec2(0,0)
end

function drawShip()
local sx = 10
local sy = 6
pushStyle()
pushMatrix()
translate(Ship.pos.x, Ship.pos.y)
strokeWidth(2)
stroke(255)
line(sx,0, -sx,sy)
line(-sx,sy, -sx,-sy)
line(-sx,-sy, sx,0)
popMatrix()
popStyle()
end

function moveShip()
if Button.fire then if not Ship.holdFire then fireMissile() end end
if not Button.fire then Ship.holdFire = false end
actualShipMove()
end

function actualShipMove()
if Button.go then
Ship.step = Ship.step + accel
Ship.step = maximize(Ship.step, 3)
end
finallyMove(Ship)
end

function finallyMove(ship)
U:moveObject(ship)
end

function maximize(vec, size)
local s = vec:len()
if s <= size then
return vec
else
return vec*size/s
end
end

function fireMissile()
Ship.holdFire = true
Missile(Ship)
end

--# Button
-- Button
-- RJ 20200520

Button = {}
local Buttons = {}

function createButtons()
local dx=50
local dy=200
table.insert(Buttons, {x=dx, y=dy, name="left"})
table.insert(Buttons, {x=dy, y=dx, name="right"})
table.insert(Buttons, {x=WIDTH-dx, y=dy, name="fire"})
table.insert(Buttons, {x=WIDTH-dy, y=dx, name = "go"})
end

function checkButtons()
Button.left = false
Button.right = false
Button.go = false
Button.fire = false
for id,touch in pairs(Touches) do
for i,button in ipairs(Buttons) do
if touch.pos:dist(vec2(button.x,button.y)) < 50 then
Button[button.name]=true
end
end
end
end

function drawButtons()
pushStyle()
textMode(CENTER)
stroke(255)
strokeWidth(1)
for i,b in ipairs(Buttons) do
pushMatrix()
pushStyle()
translate(b.x,b.y)
if Button[b.name] then
fill(128,0,0)
else
fill(128,128,128,128)
end
ellipse(0,0, 50)
fill(255)
fontSize(30)
text(b.name,0,0)
popStyle()
popMatrix()
end
popStyle()
end

--# Asteroid
-- Asteroid
-- RJ 20200520

local Vel = 1.5

function createAsteroids(asteroids)
for i = 1,4 do
local a = createAsteroid()
asteroids[a] = a
end
end

function createAsteroid()
local a = {}
a.pos = vec2(math.random(WIDTH), math.random(HEIGHT))
a.shape = Rocks[math.random(1,4)]
a.scale = 16
local angle = math.random()*2*math.pi
a.step = Ratio*vec2(Vel,0):rotate(angle)
return a
end

function drawAsteroids(asteroids)
pushStyle()
stroke(255)
fill(0,0,0, 0)
strokeWidth(2)
rectMode(CENTER)
for i,asteroid in pairs(asteroids) do
drawAsteroid(asteroid)
moveAsteroid(asteroid)
end
popStyle()
end

function killDist(asteroid)
local s = asteroid.scale
if s == 16 then return 64 elseif s == 8 then return 32 else return 16 end
end

asteroids[a] = nil
end
end

function deathSize()
local i = 0
for k, a in pairs(DeadAsteroids) do
i = i + 1
end
return i
end

function scoreAsteroid(asteroid)
local s = asteroid.scale
local inc = 0
if s == 16 then inc = 20
elseif s == 8 then inc = 50
else inc = 100
end
Score = Score + inc
end

function splitAsteroid(asteroid, asteroids)
if asteroid.scale == 4 then
Splat(asteroid.pos)
return
end
asteroid.scale = asteroid.scale//2
asteroid.angle = math.random()*2*math.pi
local new = createAsteroid()
new.pos = asteroid.pos
new.scale = asteroid.scale
asteroids[new] = new
Splat(asteroid.pos)
end

function drawAsteroid(asteroid)
pushMatrix()
pushStyle()
translate(asteroid.pos.x, asteroid.pos.y)
ellipse(0,0,2*killDist(asteroid))
scale(asteroid.scale)
strokeWidth(1/asteroid.scale)
for i,l in ipairs(asteroid.shape) do
line(l.x, l.y, l.z, l.w)
end
popStyle()
popMatrix()
end

function moveAsteroid(asteroid)
U:moveObject(asteroid)
end

--# Splat
-- Splat
-- RJ 20200521

local Splats = {}

local Vecs = {
vec2(-2,0), vec2(-2,-2), vec2(2,-2), vec2(3,1), vec2(2,-1), vec2(0,2), vec2(1,3), vec2(-1,3), vec2(-4,-1), vec2(-3,1)
}

function drawSplats()
for k, splat in pairs(Splats) do
splat:draw()
end
end

Splat = class()

function Splat:init(pos)
local die = function()
Splats[self] = nil
end
self.pos = pos
Splats[self] = self
self.size = 2
self.diameter = 6
self.rot = math.random(0,359)
tween(4, self, {size=10, diameter=1}, tween.easing.linear, die)
end

function Splat:draw()
pushStyle()
pushMatrix()
translate(self.pos.x, self.pos.y)
fill(255)
stroke(255)
rotate(self.rot)
local s = self.size
for i,v in ipairs(Vecs) do
ellipse(s*v.x, s*v.y, self.diameter)
end
popMatrix()
popStyle()
end

--# Missile
-- Missile
-- RJ 20200522

Missiles = {}

function drawMissiles()
pushStyle()
pushMatrix()
fill(255)
stroke(255)
for k, missile in pairs(Missiles) do
missile:draw()
end
popMatrix()
popStyle()
for k, missile in pairs(Missiles) do
missile:move()
end
end

Missile = class()

local MissileVelocity = 2.0

function Missile:init(ship)
function die()
self:die()
end
self.pos = ship.pos
Missiles[self] = self
tween(3, self, {}, tween.easing.linear, die)
end

function Missile:die()
Missiles[self] = nil
end

function Missile:draw()
ellipse(self.pos.x, self.pos.y, 6)
end

function Missile:move()
U:moveObject(self)
end

--# Universe
-- Universe
-- RJ 20200523

Universe = class()

function Universe:init()
self.asteroids = {}
end

function Universe:draw()
drawAsteroids(self.asteroids)
end

function Universe:createAsteroids()
createAsteroids(self.asteroids)
end

function Universe:findCollisions()
for i,a in pairs(self.asteroids) do
for k,m in pairs(Missiles) do
if m.pos:dist(a.pos) < killDist(a) then
scoreAsteroid(a)
splitAsteroid(a, self.asteroids)
m:die()
end
end
end
end

function Universe:moveObject(anObject)
local pos = anObject.pos + Ratio*anObject.step
anObject.pos = vec2(self:keepInBounds(pos.x, WIDTH), self:keepInBounds(pos.y, HEIGHT))
end

function Universe:keepInBounds(value, bound)
return (value+bound)%bound
end
``````