Skip to content

Commit

Permalink
Improve space invaders
Browse files Browse the repository at this point in the history
  • Loading branch information
iHiD committed Jan 23, 2025
1 parent 2df8b41 commit 9d09fbd
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React from 'react'
import { Exercise } from '../Exercise'
import { ExecutionContext } from '@/interpreter/executor'
import { random } from 'lodash'
import { d } from '@codemirror/legacy-modes/mode/d'

type GameStatus = 'running' | 'won' | 'lost'
type AlienStatus = 'alive' | 'dead'
class Alien {
public status: AlienStatus
public lastKilledAt?: number

public constructor(
public elem: HTMLElement,
Expand All @@ -16,6 +19,7 @@ class Alien {
this.status = 'alive'
}
}

export default class SpaceInvadersExercise extends Exercise {
private gameStatus: GameStatus = 'running'
private moveDuration = 200
Expand All @@ -30,6 +34,7 @@ export default class SpaceInvadersExercise extends Exercise {
(_, idx) => this.laserStart + idx * this.laserStep
)
private laserPosition = 0
private features = { reanimation: false }

public constructor() {
super('space-invaders')
Expand All @@ -50,6 +55,10 @@ export default class SpaceInvadersExercise extends Exercise {
return { gameStatus: this.gameStatus }
}

public enableReanimation() {
this.features.reanimation = true
}

private addAliens(rows) {
this.aliens = rows.map((row, rowIdx) => {
return row.map((type, colIdx) => {
Expand Down Expand Up @@ -77,6 +86,85 @@ export default class SpaceInvadersExercise extends Exercise {
return new Alien(alien, row, col, type)
}

private killAlien(
executionCtx: ExecutionContext,
alien: Alien,
shot: HTMLElement
) {
const deathTime = executionCtx.getCurrentTime() + this.shotDuration

alien.status = 'dead'
alien.lastKilledAt = deathTime
;[
['tl', -10, -10, -180],
['tr', 10, -10, 180],
['bl', -10, 10, -180],
['br', 10, 10, 180],
].forEach(([pos, x, y, rotate]) => {
this.addAnimation({
targets: `#${this.view.id} #${alien.elem.id} .${pos}`,
duration: 300,
transformations: {
translateX: x,
translateY: y,
rotate: rotate,
opacity: 0,
},
offset: deathTime,
})
})
this.addAnimation({
targets: `#${this.view.id} #${shot.id}`,
duration: 1,
transformations: { opacity: 0 },
offset: deathTime,
})
}

private reanimateRandomAlien(executionCtx: ExecutionContext) {
if (!this.features.reanimation) {
return
}

const deadAliens = this.aliens
.flat()
.filter(
(alien) =>
alien !== null &&
alien.status === 'dead' &&
alien.lastKilledAt &&
alien.lastKilledAt < executionCtx.getCurrentTime()
)

if (Math.random() > 0.2) {
return
} // Skip 90% of the time

// Choose random dead alien from this.aliens
const alien = deadAliens[Math.floor(Math.random() * deadAliens.length)]
if (alien == null) {
return
}

alien.status = 'alive'
const renamationTime = executionCtx.getCurrentTime() + random(200)

;['tl', 'tr', 'bl', 'br'].forEach((pos) => {
this.addAnimation({
targets: `#${this.view.id} #${alien.elem.id} .${pos}`,
duration: 1,
transformations: { translateX: 0, translateY: 0, rotate: 0 },
offset: renamationTime,
})
this.addAnimation({
targets: `#${this.view.id} #${alien.elem.id} .${pos}`,
duration: 100,
transformations: { opacity: 1 },
offset: renamationTime,
})
})
}

private moveLaser(executionCtx: ExecutionContext) {
this.addAnimation({
targets: `#${this.view.id} .laser`,
Expand Down Expand Up @@ -167,42 +255,14 @@ export default class SpaceInvadersExercise extends Exercise {
this.gameStatus = 'lost'
executionCtx.updateState('gameOver', true)
} else {
const alien = targetAlien as Alien
alien.status = 'dead'
;[
['tl', -10, -10, -180],
['tr', 10, -10, 180],
['bl', -10, 10, -180],
['br', 10, 10, 180],
].forEach(([pos, x, y, rotate]) => {
this.addAnimation({
targets: `#${this.view.id} #${alien.elem.id} .${pos}`,
duration: 300,
transformations: {
translateX: x,
translateY: y,
rotate: rotate,
opacity: 0,
},
offset: executionCtx.getCurrentTime() + duration,
})
})
this.addAnimation({
targets: `#${this.view.id} #${shot.id}`,
duration: 1,
transformations: { opacity: 0 },
offset: executionCtx.getCurrentTime() + duration,
})
this.killAlien(executionCtx, targetAlien, shot)
this.reanimateRandomAlien(executionCtx)

// Why do we do this?
executionCtx.fastForward(30)

this.checkForWin(executionCtx)
}

// const target = this.aliens[3][this.laserPosition]
// target = this.aliens[0][0]
// this.laserLeft -= this.moveStep
// if (this.laserLeft < 0) {
// }
}

public moveLeft(executionCtx: ExecutionContext) {
Expand Down
3 changes: 0 additions & 3 deletions app/javascript/interpreter/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ export function describeFrame(
frame: Frame,
externalFunctions: ExternalFunction[]
): string {
try {
console.log(process.env.NODE_ENV)
} catch {}
try {
// These need to come from the exercise.
const functionDescriptions: Record<string, string> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"slug": "scroll-and-shoot",
"name": "Scroll and Shoot",
"description_html": "Move your laser from left to right and shoot the aliens.",
"setup_functions": [["enableReanimation"]],
"checks": [
{
"name": "gameStatus",
Expand Down

0 comments on commit 9d09fbd

Please sign in to comment.