Skip to content

Instantly share code, notes, and snippets.

@litan
Last active February 4, 2023 04:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save litan/0f81274f6cc96b07ec489854e9abc60b to your computer and use it in GitHub Desktop.
Save litan/0f81274f6cc96b07ec489854e9abc60b to your computer and use it in GitHub Desktop.
Hunted - various versions
cleari()
drawStage(cm.darkGreen)
val cb = canvasBounds
val player = Picture.rectangle(50, 50)
player.setFillColor(green)
player.setPenColor(green)
player.setPosition(cb.x + cb.width / 2, cb.y + 20)
draw(player)
val nh = 5
val hunters = ArrayBuffer.empty[Picture]
val huntersVel = HashMap.empty[Picture, Vector2D]
repeatFor(1 to nh) { n =>
val pic = Picture.rectangle(50, 50)
pic.setFillColor(red)
pic.setPenColor(red)
pic.setPosition(cb.x + cb.width / (nh + 2) * n, cb.y + randomDouble(100, cb.height - 200))
hunters.append(pic)
val hv = Vector2D(random(1, 4), random(1, 4))
huntersVel(pic) = hv
draw(pic)
}
def gameLost() {
stopAnimation()
drawCenteredMessage("You Lost", white, 30)
}
val speed = 5
animate {
repeatFor(hunters) { h =>
var hv = huntersVel(h)
h.translate(hv)
if (h.collidesWith(stageBorder)) {
hv = bouncePicOffStage(h, hv)
huntersVel(h) = hv
}
if (h.collidesWith(player)) {
gameLost()
}
}
if (isKeyPressed(Kc.VK_RIGHT)) {
player.translate(speed, 0)
}
if (isKeyPressed(Kc.VK_LEFT)) {
player.translate(-speed, 0)
}
if (isKeyPressed(Kc.VK_UP)) {
player.translate(0, speed)
}
if (isKeyPressed(Kc.VK_DOWN)) {
player.translate(0, -speed)
}
if (player.collidesWith(stageBorder)) {
gameLost()
}
}
showGameTime(10, "You Win", black, 25)
activateCanvas()
cleari()
drawStage(cm.darkGreen)
val cb = canvasBounds
case class Player(var x: Double, var y: Double)
case class Hunter(var x: Double, var y: Double, var vel: Vector2D)
val player = Player(cb.x + cb.width / 2, cb.y + 20)
val hunters = ArrayBuffer.empty[Hunter]
var playerPic: Picture = Picture.rectangle(0, 0)
val hunterPics = ArrayBuffer.empty[Picture]
var lost = false
val nh = 5
repeatFor(1 to nh) { n =>
val hv = Vector2D(random(1, 4), random(1, 4))
val hunter = Hunter(
cb.x + cb.width / (nh + 2) * n,
cb.y + randomDouble(100, cb.height - 200),
hv
)
hunters.append(hunter)
}
def gameLost() {
stopAnimation()
drawCenteredMessage("You Lost", white, 30)
}
def updateState() {
var idx = 0
repeatFor(hunters) { h =>
val hv = h.vel
val hp = hunterPics(idx)
if (hp.collidesWith(stageBorder)) {
h.vel = bouncePicOffStage(hp, h.vel)
}
if (hp.collidesWith(playerPic)) {
lost = true
}
h.x += h.vel.x
h.y += h.vel.y
idx += 1
}
if (isKeyPressed(Kc.VK_RIGHT)) {
player.x += speed
}
if (isKeyPressed(Kc.VK_LEFT)) {
player.x -= speed
}
if (isKeyPressed(Kc.VK_UP)) {
player.y += speed
}
if (isKeyPressed(Kc.VK_DOWN)) {
player.y -= speed
}
if (playerPic.collidesWith(stageBorder)) {
lost = true
}
}
def drawView() {
if (lost) {
gameLost()
return
}
playerPic.erase()
repeatFor(hunterPics) { pic => pic.erase() }
playerPic = Picture.rectangle(50, 50)
.withPosition(player.x, player.y)
.withPenColor(green)
.withFillColor(green)
hunterPics.clear()
repeatFor(hunters) { hunter =>
val hunterPic = Picture.rectangle(50, 50)
.withPosition(hunter.x, hunter.y)
.withPenColor(red)
.withFillColor(red)
hunterPics.append(hunterPic)
}
playerPic.draw()
repeatFor(hunterPics) { pic => pic.draw() }
}
val speed = 5
drawView()
animate {
updateState()
drawView()
}
showGameTime(10, "You Win", black, 25)
activateCanvas()
cleari()
drawStage(cm.darkGreen)
val cb = canvasBounds
case class Player(x: Double, y: Double)
case class Hunter(x: Double, y: Double, vel: Vector2D)
case class GameState(
player: Player,
hunters: Seq[Hunter],
lost: Boolean
)
case class GamePics(
playerPic: Picture,
hunterPics: Seq[Picture],
lostPic: Option[Picture]
) {
def draw() {
playerPic.draw()
repeatFor(hunterPics) { pic => pic.draw() }
lostPic.foreach { pic => pic.draw() }
}
def erase() {
playerPic.erase()
repeatFor(hunterPics) { pic => pic.erase() }
lostPic.foreach { pic => pic.erase() }
}
}
// this is available in Kojo 2.9.24
def makeCenteredMessage(message: String, color: Color = black, fontSize: Int): Picture = {
val te = textExtent(message, fontSize)
PicShape.text(message, fontSize)
.withTranslation(cb.x + (cb.width - te.width) / 2, cb.y + (cb.height - te.height) / 2 + te.height)
.withPenColor(color)
}
def gameLostMsg: Picture = {
makeCenteredMessage("You Lost", white, 30)
}
def initState(nh: Int): GameState = {
val player = Player(cb.x + cb.width / 2, cb.y + 20)
val hunters = (1 to nh).map { n =>
val hv = Vector2D(random(1, 4), random(1, 4))
Hunter(
cb.x + cb.width / (nh + 2) * n,
cb.y + randomDouble(100, cb.height - 200),
hv
)
}
GameState(player, hunters, false)
}
def nextState(prevState: GameState, gpics: GamePics): GameState = {
val hunters = prevState.hunters
val playerPic = gpics.playerPic
val hunterPics = gpics.hunterPics
val gameLost =
hunters.zipWithIndex.find { case (h, idx) =>
val hp = hunterPics(idx)
if (hp.collidesWith(playerPic)) true else false
}.isDefined ||
playerPic.collidesWith(stageBorder)
if (gameLost) {
GameState(prevState.player, prevState.hunters, true)
}
else {
val newHunters = hunters.zipWithIndex.map { case (h, idx) =>
val hv = h.vel
val hp = hunterPics(idx)
val hv2 = if (hp.collidesWith(stageBorder))
bouncePicOffStage(hp, hv)
else
hv
val hx2 = h.x + hv2.x
val hy2 = h.y + hv2.y
Hunter(hx2, hy2, hv2)
}
var newPlayer = prevState.player
if (isKeyPressed(Kc.VK_RIGHT)) {
newPlayer = newPlayer.copy(x = newPlayer.x + speed)
}
if (isKeyPressed(Kc.VK_LEFT)) {
newPlayer = newPlayer.copy(x = newPlayer.x - speed)
}
if (isKeyPressed(Kc.VK_UP)) {
newPlayer = newPlayer.copy(y = newPlayer.y + speed)
}
if (isKeyPressed(Kc.VK_DOWN)) {
newPlayer = newPlayer.copy(y = newPlayer.y - speed)
}
GameState(newPlayer, newHunters, false)
}
}
def statePic(gstate: GameState): GamePics = {
val player = gstate.player
val hunters = gstate.hunters
val playerPic = Picture.rectangle(50, 50)
.withPosition(player.x, player.y)
.withPenColor(green)
.withFillColor(green)
val hunterPics = hunters.map { hunter =>
Picture.rectangle(50, 50)
.withPosition(hunter.x, hunter.y)
.withPenColor(red)
.withFillColor(red)
}
var gpics = GamePics(playerPic, hunterPics, None)
if (gstate.lost)
gpics.copy(lostPic = Some(gameLostMsg))
else
gpics
}
val speed = 5
val iState = initState(5)
val iPics = statePic(iState)
iPics.draw()
animateWithState(iState, iPics) { case (gameState, gamePics) =>
val gameState2 = nextState(gameState, gamePics)
val gamePics2 = statePic(gameState2)
gamePics.erase()
gamePics2.draw()
if (gameState2.lost) {
stopAnimation()
}
(gameState2, gamePics2)
}
showGameTime(10, "You Win", black, 25)
activateCanvas()
cleari()
drawStage(cm.darkGreen)
val cb = canvasBounds
class Player(x0: Double, y0: Double) {
val speed = 5
private var x = x0
private var y = y0
private var pic = Picture.rectangle(50, 50)
.withPenColor(green)
.withFillColor(green)
pic.setPosition(x, y)
def viewPic = pic
def step() {
if (isKeyPressed(Kc.VK_RIGHT)) {
x += speed
}
if (isKeyPressed(Kc.VK_LEFT)) {
x -= speed
}
if (isKeyPressed(Kc.VK_UP)) {
y += speed
}
if (isKeyPressed(Kc.VK_DOWN)) {
y -= speed
}
if (pic.collidesWith(stageBorder)) {
gameLost = true
}
}
def view() {
if (pic.isDrawn) {
pic.setPosition(x, y)
}
else {
pic.draw()
}
}
}
class Hunter(x0: Double, y0: Double, vel0: Vector2D) {
private var x = x0
private var y = y0
private var vel = vel0
private var pic = Picture.rectangle(50, 50)
.withPenColor(red)
.withFillColor(red)
pic.setPosition(x, y)
def step() {
if (pic.collidesWith(stageBorder)) {
vel = bouncePicOffStage(pic, vel)
}
if (pic.collidesWith(player.viewPic)) {
gameLost = true
}
x += vel.x
y += vel.y
}
def view() {
if (pic.isDrawn) {
pic.setPosition(x, y)
}
else {
pic.draw()
}
}
}
val player = new Player(cb.x + cb.width / 2, cb.y + 20)
val nh = 5
val hunters = (1 to nh).map { n =>
val hv = Vector2D(random(1, 4), random(1, 4))
new Hunter(
cb.x + cb.width / (nh + 2) * n,
cb.y + randomDouble(100, cb.height - 200),
hv
)
}
var gameLost = false
def showGameLost() {
stopAnimation()
drawCenteredMessage("You Lost", white, 30)
}
def updateState() {
repeatFor(hunters) { h => h.step() }
player.step()
}
def viewState() {
if (gameLost) {
showGameLost()
}
else {
player.view()
repeatFor(hunters) { h => h.view() }
}
}
viewState()
animate {
updateState()
viewState()
}
showGameTime(10, "You Win", black, 25)
activateCanvas()
cleari()
drawStage(cm.darkGreen)
val cb = canvasBounds
case class PlayerState(x: Double, y: Double) {
def withUpdatedX(speed: Double): PlayerState = copy(x = x + speed)
def withUpdatedY(speed: Double): PlayerState = copy(y = y + speed)
}
case class HunterState(x: Double, y: Double, vel: Vector2D) {
def withVel(vel2: Vector2D): HunterState = copy(vel = vel2)
def withVelUpdatedXY: HunterState = copy(x = x + vel.x, y = y + vel.y)
}
class Player(x0: Double, y0: Double) {
val speed = 5
private var state = PlayerState(x0, y0)
private var pic = Picture.rectangle(50, 50)
pic.setPenColor(green)
pic.setFillColor(green)
pic.setPosition(state.x, state.y)
def viewPic = pic
def step() {
var newState = state
if (isKeyPressed(Kc.VK_RIGHT)) {
newState = newState.withUpdatedX(speed)
}
if (isKeyPressed(Kc.VK_LEFT)) {
newState = newState.withUpdatedX(-speed)
}
if (isKeyPressed(Kc.VK_UP)) {
newState = newState.withUpdatedY(speed)
}
if (isKeyPressed(Kc.VK_DOWN)) {
newState = newState.withUpdatedY(-speed)
}
if (pic.collidesWith(stageBorder)) {
gameLost = true
}
state = newState
}
def view() {
if (pic.isDrawn) {
pic.setPosition(state.x, state.y)
}
else {
pic.draw()
}
}
}
class Hunter(x0: Double, y0: Double, vel0: Vector2D) {
private var state = HunterState(x0, y0, vel0)
private var pic = Picture.rectangle(50, 50)
pic.setPenColor(red)
pic.setFillColor(red)
pic.setPosition(state.x, state.y)
def step() {
var newState = state
if (pic.collidesWith(stageBorder)) {
val newVel = bouncePicOffStage(pic, newState.vel)
newState = newState.withVel(newVel)
}
if (pic.collidesWith(player.viewPic)) {
gameLost = true
}
newState = newState.withVelUpdatedXY
state = newState
}
def view() {
if (pic.isDrawn) {
pic.setPosition(state.x, state.y)
}
else {
pic.draw()
}
}
}
val player = new Player(cb.x + cb.width / 2, cb.y + 20)
val nh = 5
val hunters = (1 to nh).map { n =>
val hv = Vector2D(random(1, 4), random(1, 4))
new Hunter(
cb.x + cb.width / (nh + 2) * n,
cb.y + randomDouble(100, cb.height - 200),
hv
)
}
var gameLost = false
def showGameLost() {
stopAnimation()
drawCenteredMessage("You Lost", white, 30)
}
def updateState() {
repeatFor(hunters) { h => h.step() }
player.step()
}
def viewState() {
if (gameLost) {
showGameLost()
}
else {
player.view()
repeatFor(hunters) { h => h.view() }
}
}
viewState()
animate {
updateState()
viewState()
}
showGameTime(10, "You Win", black, 25)
activateCanvas()
cleari()
drawStage(cm.white)
val cb = canvasBounds
// game model/state
case class Player(x: Double, y: Double, w: Double, h: Double)
case class Hunter(x: Double, y: Double, w: Double, h: Double, vel: Vector2D)
case class Model(
player: Player,
hunters: Seq[Hunter],
gameOver: Boolean
)
// possible events/messages that can update the model
trait Msg
case object Tick extends Msg
case object MoveLeft extends Msg
case object MoveRight extends Msg
case object MoveUp extends Msg
case object MoveDown extends Msg
case object DontMove extends Msg
val nh = 20
def init: Model =
Model(
Player(cb.x + cb.width / 2, cb.y + 20, 40, 40),
(1 to nh).map { n =>
Hunter(
cb.x + cb.width / (nh + 2) * n,
cb.y + randomDouble(100, cb.height - 200),
40, 40,
Vector2D(random(1, 4), random(1, 4))
)
},
false
)
val speed = 5
val cd = new net.kogics.kojo.gaming.CollisionDetector()
def update(m: Model, msg: Msg): Model = msg match {
case MoveLeft =>
val player = m.player
m.copy(player = player.copy(x = player.x - speed))
case MoveRight =>
val player = m.player
m.copy(player = player.copy(x = player.x + speed))
case MoveUp =>
val player = m.player
m.copy(player = player.copy(y = player.y + speed))
case MoveDown =>
val player = m.player
m.copy(player = player.copy(y = player.y - speed))
case DontMove => m
case Tick =>
val newm = m.copy(hunters =
m.hunters.map { h =>
val newx = h.x + h.vel.x
val newy = h.y + h.vel.y
val vx = if (cd.collidesWithHorizontalEdge(newx, h.w))
h.vel.x * -1 else h.vel.x
val vy = if (cd.collidesWithVerticalEdge(newy, h.h))
h.vel.y * -1 else h.vel.y
h.copy(x = newx, y = newy, vel = Vector2D(vx, vy))
})
val p = m.player
val gameOver =
cd.collidesWithEdge(p.x, p.y, p.w, p.h) ||
newm.hunters.exists { h =>
cd.collidesWith(p.x, p.y, p.w, p.h, h.x, h.y, h.w, h.h)
}
newm.copy(gameOver = gameOver)
}
def playerPic(p: Player): Picture = {
val base = Picture.rectangle(p.w, p.h)
return base.thatsFilledWith(cm.yellow).thatsStrokeColored(black).thatsTranslated(p.x, p.y)
}
def hunterPic(h: Hunter): Picture = {
val base = Picture.rectangle(h.w, h.h)
return base.thatsFilledWith(cm.lightBlue).thatsStrokeColored(black).thatsTranslated(h.x, h.y)
}
def view(m: Model): Picture = {
val viewPics =
m.hunters.map { h =>
hunterPic(h)
}.appended(playerPic(m.player))
if (m.gameOver) {
picStack(viewPics.appended(Picture.text("Game Over", 40)))
}
else
picStack(viewPics)
}
val tickSub = Subscriptions.onAnimationFrame {
Tick
}
val keyDownSub = Subscriptions.onKeyDown { keyCode =>
keyCode match {
case Kc.VK_LEFT => MoveLeft
case Kc.VK_RIGHT => MoveRight
case Kc.VK_UP => MoveUp
case Kc.VK_DOWN => MoveDown
case _ => DontMove
}
}
def subscriptions(m: Model) = {
if (m.gameOver) Seq()
else Seq(tickSub, keyDownSub)
}
runGame(init, update, view, subscriptions)
activateCanvas()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment