Robots/Julia
< Robots
Julia
Uses the Gtk library.
using Gtk, Colors, Cairo, Graphics
const fontpointsize = 10
const mapwidth = 900
const mapheight = 700
const leveldim = div(mapwidth, fontpointsize)
const windowmaxx = div(mapheight, fontpointsize)
const windowmaxy = leveldim
win = GtkWindow("Robots Game (H for Help)", mapwidth, mapheight) |> (GtkFrame() |> (vbox = GtkBox(:v)))
set_gtk_property!(vbox, :expand, true)
can = GtkCanvas(div(mapwidth, 3), mapheight)
push!(vbox, can)
const playerchar, robotchar, hazardchar, wallchar, spacechar = '@', '+', '\u2623', '\u2593', ' '
const itemcolors = Dict{Char, Colorant}('@' => colorant"white", ' ' => colorant"black",
'\u2623' => colorant"gold", '+' => colorant"red", '\u2593' => colorant"silver")
const debrisprobability, debrisdiameter, maxrobots, gravechar = 0.005, 6, 10, '\u271d'
const gameover = [false]
const grid = fill(spacechar, windowmaxx, windowmaxy)
@guarded draw(can) do widget
ctx = getgc(can)
select_font_face(ctx, "Courier", Cairo.FONT_SLANT_NORMAL, Cairo.FONT_WEIGHT_BOLD)
set_font_size(ctx, fontpointsize)
workcolor = colorant"black"
set_source_rgb(ctx, 0.2, 0.2, 0.2)
rectangle(ctx, 0, 0, mapwidth, mapheight)
fill(ctx)
color = colorant"white"
set_source(ctx, color)
linelen = size(grid)[2]
workbuf = Char[]
for i in 1:size(grid)[1]
move_to(ctx, 0, i * fontpointsize)
lastcharprinted = '\x01'
for j in 1:linelen
ch = grid[i, j]
if j == 1
lastcharprinted = ch
elseif ch != lastcharprinted
show_text(ctx, String(workbuf))
empty!(workbuf)
end
if haskey(itemcolors, ch) && itemcolors[ch] != color
color = itemcolors[ch]
set_source(ctx, color)
end
push!(workbuf, ch)
if j == linelen
show_text(ctx, String(workbuf))
empty!(workbuf)
end
end
end
end
struct Point
x::Int
y::Int
end
randomlocation() = Point(rand(2:windowmaxx-1), rand(2:windowmaxy))
function randomdiameterfill(n, center, ch)
d = rand(1:n)
surround = [Point(i + center.x, j + center.y) for i in -d:d, j in -d:d]
for p in surround
if p.x > 1 && p.x < windowmaxx -1 && p.y > 1 && p.y < windowmaxy -1
if rand() * d > sqrt((center.x - p.x)^2 + (center.y - p.y)^2)
grid[p.x, p.y] = ch
end
end
end
end
function randomemptypoint()
for i in 1:100000
pt = randomlocation()
if grid[pt.x, pt.y] == spacechar
return pt
end
end
throw("Cannot find an empty point in the grid")
end
mutable struct Player
location::Point
movenumber::Int
lastteleport::Int
Player() = new(randomemptypoint(), 1, 0)
end
mutable struct Robot
location::Point
active::Bool
end
function setupgrid()
x1, y1, x2, y2, robots = 1, 1, windowmaxx, windowmaxy, Robot[]
grid[x1:x2, y1:y2] .= ' '
grid[x1:x2, y1] .= wallchar
grid[x1:x2, y2] .= wallchar
grid[x1, y1:y2] .= wallchar
grid[x2, y1:y2] .= wallchar
for x in x1:x2, y in y1:y2
if rand() < debrisprobability
randomdiameterfill(debrisdiameter, Point(x, y), hazardchar)
end
end
for _ in 1:maxrobots
p = randomemptypoint()
grid[p.x, p.y] = robotchar
push!(robots, Robot(p, true))
end
return Player(), robots
end
function updategrid!(player)
grid[player.location.x, player.location.y] = playerchar
Gtk.showall(win)
draw(can)
Gtk.show(can)
end
function help(player)
info_dialog("W up, S down, A left, D right, Spacebar teleport (every 12 moves).\n" *
" Avoid Robots and Hazards! Good Luck!", win)
end
playerwins(p) = warn_dialog("You have WON the game!\nTotal moves: $(p.movenumber)", win)
playerloses(p) = warn_dialog("You have lost the game.\nTotal moves: $(p.movenumber)", win)
function destroyrobot!(robots, x, y)
if grid[x, y] == robotchar
grid[x, y] = hazardchar
for (i, robot) in enumerate(robots)
if robot.location.x == x && robot.location.y == y
robots[i].active = false
break
end
end
end
end
function moverobot!(robots, robot, dx, dy, player)
(dx == 0 && dy == 0) && return false
p = Point(robot.location.x + dx, robot.location.y + dy)
if grid[p.x, p.y] == wallchar
return false
elseif grid[p.x, p.y] == spacechar # robot moves a space
grid[robot.location.x, robot.location.y] = spacechar
grid[p.x, p.y] = robotchar
robot.location = Point(p.x, p.y)
return true
elseif grid[p.x, p.y] == robotchar # robots collide
destroyrobot!(robots, robot.location.x, robot.location.y)
destroyrobot!(robots, p.x, p.y)
elseif grid[p.x, p.y] == hazardchar # robot hits hazard
destroyrobot!(robots, robot.location.x, robot.location.y)
elseif grid[p.x, p.y] == playerchar # robot hits player
grid[p.x, p.y] = gravechar
gameover[1] = true
playerloses(player)
end
if all(r -> !r.active, robots)
gameover[1] = true
updategrid!(player)
playerwins(player)
end
return false
end
function robotsmove!(robots, player)
for robot in robots
if robot.active
dx = player.location.x - robot.location.x
dy = player.location.y - robot.location.y
xmove = dx > 0 ? 1 : dx == 0 ? 0 : -1
ymove = dy > 0 ? 1 : dy == 0 ? 0 : -1
if !moverobot!(robots, robot, xmove, ymove, player)
moverobot!(robots, robot, rand([-1, 0, 1]), rand([-1, 0, 1]), player)
end
else
grid[robot.location.x, robot.location.y] = hazardchar
end
end
end
function playerteleport(player)
if player.lastteleport < player.movenumber
p = randomemptypoint()
grid[player.location.x, player.location.y] = spacechar
grid[p.x, p.y] = playerchar
player.location = p
player.movenumber += 1
player.lastteleport = player.movenumber + 12
updategrid!(player)
end
end
function playermove(player, dx, dy)
goalp = Point(player.location.x + dx, player.location.y + dy)
if grid[goalp.x, goalp.y] != wallchar
if grid[goalp.x, goalp.y] == hazardchar || grid[goalp.x, goalp.y] == robotchar
grid[player.location.x, player.location.y] = gravechar
gameover[1] = true
updategrid!(player)
playerloses(player)
else
grid[player.location.x, player.location.y] = spacechar
player.location = Point(goalp.x, goalp.y)
grid[goalp.x, goalp.y] = playerchar
end
end
player.movenumber += 1
end
playerN(player) = playermove(player, -1, 0)
playerW(player) = playermove(player, 0, -1)
playerS(player) = playermove(player, 1, 0)
playerE(player) = playermove(player, 0, 1)
const usercommands = Dict("W" => playerN, "S" => playerS, "D" => playerE, "A" => playerW,
" " => playerteleport, "H" => help)
const inputchan = Channel{String}(1000)
kbinput(w, event) = (push!(inputchan, string(Char(event.keyval))); 1)
const inputhid = signal_connect(kbinput, win, "key-press-event")
getinput(choices) = while true if (c = uppercase(take!(inputchan))) in choices return c end; end
function rungame()
player, robots = setupgrid()
while !gameover[1]
robotsmove!(robots, player)
updategrid!(player)
choice = getinput(keys(usercommands))
usercommands[choice](player)
end
end
rungame()