Remote agent/Agent logic/Julia

From Rosetta Code
Revision as of 08:06, 11 July 2019 by Wherrera (talk | contribs) (julia example)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

The agent uses a random walk algorithm, to which is added a preference for unknown tiles, to explore the board, and the following:
When the agent does not have a ball, the agent will tend to move toward a sector on a straight line path from agent (if such a sector has a mismatch between sector and ball colors), to get the next ball for agent.
When the agent holds a ball, it will have a preference for straight line paths leading to an empty sector the color of the agent's ball in order to drop the ball. <lang julia>using Distributed, Gtk, Colors, Cairo

include("RemoteGameServerClient.jl")

using .RemoteGameServerClient

const dirorder = Dict(North => 1, East => 2, South => 3, West => 4) const orderdir = Dict(1 => North, 2 => East, 3 => South, 4 => West) const rightturns = [[North, East], [East, South], [South, West], [West, North]] const leftturns = [[East, North], [South, East], [West, South], [North, West]] const turnpi = [[East, West], [South, North], [West, East], [North, South]] const dlabl = Dict(North => "north", East => "east", South => "south", West => "west") const won = [false]

function ballhandler(grid)

   sector = grid.mat[grid.agent.location.x, grid.agent.location.y]
   agent = grid.agent
   if sector.ch == configs["emptytile"] && sector.clr != sector.ball
       if sector.ball == nocolor && agent.ball == sector.clr
           sector.ball = agent.ball
           agent.ball = nocolor
           return [Drop]
       elseif sector.ball != nocolor && agent.ball == nocolor
           agent.ball = sector.ball
           sector.ball = nocolor
           return [Get]
       end
   end
   Char[]

end

function turn(grid, from::Direction, to::Direction)

   grid.agent.direction = to
   [from, to] in rightturns ? [TurnRight] :
   [from, to] in leftturns ? [TurnLeft] :
   [from, to] in turnpi ? [TurnRight, TurnRight] :
   Char[]

end

forhaswrongball(sector, agent) = sector.ball == nocolor && sector.clr == agent.ball forhasnoball(sector, agent) = sector.ball != nocolor && sector.clr != sector.ball

function sectortoward(grid, ax, ay, dir, f)

   x, y = ax + dir[1], ay + dir[2]
   while grid.mat[x, y].ch == configs["emptytile"]
       if f(grid.mat[x, y], grid.agent)
           return true
       end
       x, y = x + dir[1], y + dir[2]
   end
   return false

end

function chooseforward(grid)

   ret = ballhandler(grid)
   ag = grid.agent
   nearby = [grid.mat[a[1], a[2]].ch for a in adjacent(ag.location.x, ag.location.y)]
   allunknown = [orderdir[i] for i in 1:length(nearby) if nearby[i] == configs["unknowntile"]]
   allempty = [orderdir[i] for i in 1:length(nearby) if nearby[i] == configs["emptytile"]]
   grid.turncount += 1
   if nearby[dirorder[ag.direction]] == configs["unknowntile"]
       return vcat(ret, [Forward])
   elseif length(allunknown) > 0
       return vcat(ret, turn(grid, ag.direction, rand(allunknown)), [Forward])
   elseif length(allempty) > 0
       x, y = ag.location.x, ag.location.y
       f = (ag.ball == nocolor) ? forhasnoball : forhaswrongball
       for dir in allempty
           if sectortoward(grid, x, y, dir, f)
               return vcat(ret, turn(grid, ag.direction, dir), [Forward])
           end
       end
       return vcat(ret, turn(grid, ag.direction, rand(allempty)), [Forward])
   else
       throw("Cannot find a way out from location ", ag.location)
   end

end

function makeunknowngrid(height, width)

   m = Matrix{Sector}(undef, height, width)
   for i in 1:height, j in 1:width
       m[i, j] = ((i == 1) || (j == 1) || (i == height) || (j == width)) ?
           Sector(configs["walltile"], configs["wallcolor"], nocolor) :
           Sector(configs["unknowntile"], configs["unknowncolor"], nocolor)
   end
   Grid(m, Agent(Pt(height ÷ 2, width ÷ 2), North, nocolor), 0)

end

const scolordict = Dict(p[2] => p[1] for p in colorsectors) const ballcolordict = Dict(lowercase(p[2]) => p[1] for p in colorsectors) nullexec(c, g, s, l) = () warnexec(c, g, s, l) = @warn("Server told client command was in error because $c") ballexec(c, g, s, l) = begin s.ball = ballcolordict[c] end

function bumpexec(c, g, s, l)

   if s.ch == configs["emptytile"]
       throw("Bump for a sector at $newlocation we marked empty ($newsector)")
   end
   s.ch, s.clr, s.ball = configs["walltile"], configs["wallcolor"], nocolor

end

function intosectorexec(c, g, s, l)

   if s.ch == configs["walltile"]
       throw("The sector at $l was marked as a wall, now is marked empty")
   end
   g.agent.location = l
   s.ch = configs["emptytile"]
   s.clr = scolordict[c]

end

wonexec(c, g, s, l) = won[1] = true

const charexec = Dict{Char, Function}(GameOver => wonexec,

   Bump => bumpexec, SectorFull => warnexec, AgentFull => warnexec,
   NoSectorBall => warnexec, NoAgentBall => warnexec,
   SectorRed => intosectorexec, SectorGreen => intosectorexec,
   SectorYellow => intosectorexec, SectorBlue => intosectorexec,
   BallRed => ballexec, BallGreen => ballexec,
   BallYellow => ballexec, BallBlue => ballexec)

function processreplies(chars, grid)

   newx = grid.agent.location.x + grid.agent.direction[1]
   newy = grid.agent.location.y + grid.agent.direction[2]
   newsector, newlocation = grid.mat[newx, newy], Pt(newx, newy)
   for cmd in chars
       if cmd == Stop
           break
       end
       charexec[cmd](cmd, grid, newsector, newlocation)
   end

end

function matchballsgameclient()

   # The agent starts facing north, but in a random location on map, with the map
   # height and width randomly excahnged. Therefore, starting facing north gives
   # no useful information about the map contents, since its shape is not known.
   height = maximum(configs["dimensions"]) * 2 # big enough for any
   width, fontsize = height, configs["dimensions"][1] * 2
   win = GtkWindow("Match Color Balls Game Client Running     ", 600, 600) |>
       (GtkFrame() |> (box = GtkBox(:v)))
   can = GtkCanvas()
   set_gtk_property!(can, :expand, true)
   push!(box, can)
   grid = makeunknowngrid(height, width)
   @guarded Gtk.draw(can) do widget
       ctx = Gtk.getgc(can)
       select_font_face(ctx, "Courier", Cairo.FONT_SLANT_NORMAL, Cairo.FONT_WEIGHT_BOLD)
       set_source_rgb(ctx, 0.2, 0.2, 0.2)
       l = fontsize * 2.5
       for i in 1:size(grid.mat)[1], j in 1:size(grid.mat)[2]
           set_source(ctx, grid.mat[i, j].clr)
           rectangle(ctx, (i - 1) * l, (j - 1) * l, l, l)
           fill(ctx)
           if hasball(grid.mat[i, j])
               set_source(ctx, grid.mat[i, j].ball)
               circle(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 1.25, fontsize)
               fill(ctx)
               set_source(ctx, colorant"gray")
               arc(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 1.25, fontsize, 0, 2*pi)
               stroke(ctx)
           end
           if Pt(i, j) == grid.agent.location
               move_to(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 0.2)
               set_source(ctx, colorant"silver")
               line_to(ctx, i * l, j * l)
               line_to(ctx, (i - 1) * l, j * l)
               line_to(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 0.2)
               stroke(ctx)
               set_source(ctx, (grid.agent.ball == nocolor) ? colorant"black" : grid.agent.ball)
               circle(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 2, fontsize / 4)
               fill(ctx)
               set_source(ctx, colorant"silver")
               arc(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 2, fontsize / 4, 0, 2*pi)
               stroke(ctx)
           end
           if grid.mat[i, j].ch == configs["unknowntile"]
               move_to(ctx, (i - 1) * l + fontsize * 0.8 , (j - 1) * l + fontsize * 2)
               set_font_size(ctx, fontsize * 1.5)
               set_source(ctx, colorant"silver")
               show_text(ctx, "?")
           end
       end
   end
   function clientsendcommand(grid, inchan, outchan, debug=false)
       debug && println("Starting client game play.")
       debug && println("Configuration settings are: $(RemoteGameServerClient.configs).")
       while isopen(inchan) && isopen(outchan)
           draw(can)
           show(can)
           show(win)
           debug && println("Currently agent is facing ", grid.agent.direction, " holding ", grid.agent.ball)
           cmds = chooseforward(grid)
           for ch in cmds
               draw(can)
               show(can)
               show(win)
               debug && println("Sending command as char $ch")
               put!(outchan, ch)
               ch, reply = '\0', Char[]
               while (ch = take!(inchan)) != '.'
                   push!(reply, ch)
               end
               push!(reply, ch) # '.'
               debug && println("clientsendcommand: Reply from server is $reply")
               processreplies(reply, grid)
               if won[1] == true
                   println("Game over, agent won in ", grid.turncount, " moves.")
                   warn_dialog("You have WON the game!\nTotal moves: $(grid.turncount)", win)
                   close(inchan)
                   close(outchan)
               end
           end
       end
   end
   grid = makeunknowngrid(height, width)
   serverin, serverout = Channel{Char}(100), Channel{Char}(100)
   @async asyncclientsocketIO(serverin, serverout, false)
   @async clientsendcommand(grid, serverin, serverout, false)
   condition = Condition()
   endit(w) = notify(condition)
   signal_connect(endit, win, :destroy)
   showall(win)
   wait(condition)

end

matchballsgameclient() </lang>