Game

Here is a short program written in Ki.

import math

const LAVA_SPOT_COUNT:  6
const PLAYER_COUNT:     2
const ENEMY_COUNT:      4
const AMMO:            10

struct Size {
    var width:  uint(0)
    var height: uint(0)
    var depth:  uint(0)
}

struct Point {
    var x: uint(0)
    var y: uint(0)
    var z: uint(0)

    static fn random(Point min, Point max) (*Point!) {
        var point: *Point(
            math.rand_uint_range(min.x, max.x)
            math.rand_uint_range(min.y, max.y)
            math.rand_uint_range(min.z, max.z)
        )

        return *point!
    }

    :eq fn(Point other) (bool) {
        return self.x == other.x && self.y == other.y && self.z == other.z
    }

    fn randomize(Point min, Point max) {
        self.x = math.rand_uint_range(min.x, max.x)
        self.y = math.rand_uint_range(min.y, max.y)
        self.z = math.rand_uint_range(min.z, max.z)
    }
}

component MapInhabiter {
    :requirements {
        val position: Point
        val map: &Map
    }

    get position (Point) {
        return Point
    }

    set position (Point new_point) {
        if (new_point.x >= self.map.size.width  ||
            new_point.y >= self.map.size.height ||
            new_point.z >= self.map.size.depth) {
            error({
                "Position ${new_point.to_string()} outside bounds "
                "${self.map.size.to_string()}."
            })
        }
        self.position.x = new_point.x
        self.position.y = new_point.y
        self.position.z = new_point.z
    }

    set map (Map &new_map) {
        self.map = new_map
    }

    pred is_in_lava {
        for (lava_spot in self.map.lava_spots) {
            if (self.position == lava_spot) {
                return true
            }
        }

        return false
    }
}

struct Enemy {
    val position: Point

    :components {
        MapInhabiter
    }

    static fn at_random_position(Point min, Point max) (*Enemy) {
        val enemy: *Enemy(
            position: Point.random(min, max)
        )

        return *enemy
    }
}

struct Player {
    var position: Point
    var ammo:     u8(0)

    :components {
        MapInhabiter
    }

    static fn at_random_position(Point min, Point max, uint ammo) (*Player!) {
        var player: *Player(
            position: Point.random(min, max),
            ammo: ammo
        )

        return *player!
    }

    fn move() {
        @overflow.limit {
            self.position.x += 14
        }
    }

    fn shoot() {
        @overflow.limit {
            self.ammo--
        }
    }
}

array LavaSpotArray<Point>

dynarray EnemyArray<*Enemy>

dynarray PlayerArray<*Player!>

struct Map {
    val size:        Size
    val lava_spots: *LavaSpotArray
}

struct Game {
    var map:     Map
    var players: PlayerArray
    var enemies: EnemyArray

    fn add_player(Player *p!) {
        self.players.append(*p!)
    }

    fn add_enemy(Enemy *e) {
        self.enemies.append(*e)
    }

    fn tick() {
        with (self.players.create_deletion_list() as player_delist) {
            for (i, player in self.players) {
                player.move()

                if (self.map.in_lava(player)) {
                    echo("Player ${i + 1} fell in the lava!")
                    player_delist.add(i)
                    continue
                }

                player.shoot()

                with (self.enemies.create_deletion_list() as enemy_delist) {
                    for (j, enemy in self.enemies) {
                        if (enemy.position.x == player.position.x) {
                            echo("Player ${i + 1} shot enemy ${j + 1}!")
                            enemy_delist.add(j)
                            continue
                        }
                    }
                }
            }
        }
    }
}

fn main() {
    val msize: Size(20, 1, 20)
    val zero: Point()
    val max: Point(msize.width - 1, msize.height - 1, msize.depth - 1)
    var game: Game(
        map: Map(
            size: msize
            lava_spots: *PointArray(count: LAVA_SPOT_COUNT)
        )
    }

    for (&point! in lava_spots) {
        point.randomize(zero, max)
    }

    loop (PLAYER_COUNT) {
        game.add_player(*Player.at_random_position(zero, max, AMMO)!)
    }

    loop (ENEMY_COUNT) {
        game.add_enemy(*Enemy.at_random_position(zero, max))
    }

    while ((game.players.length > 0) && (enemies.players.length > 0)) {
        game.tick()
    }

    if (game.players.length <= 0) {
        echo("All players died, enemies win :(")
    }
    else {
        echo("All enemies died, players win :)")
    }
}

# vi: set et sw=4 ts=4 tw=79: