Robots/Julia

From Rosetta Code
Revision as of 05:09, 12 February 2020 by Wherrera (talk | contribs) (add help dialog)

Julia
Uses the Gtk library. <lang julia>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() </lang>