Remote agent/Simulation/Julia: Difference between revisions

From Rosetta Code
Content added Content deleted
(julia example)
 
mNo edit summary
Line 47: Line 47:
colorant"blue", colorant"yellow", colorant"transparent"
colorant"blue", colorant"yellow", colorant"transparent"
const sectorcolors = [Red, Green, Blue, Yellow]
const sectorcolors = [Red, Green, Blue, Yellow]
const colorsectors = Dict{Color,Char}(Red => SectorRed, Green => SectorGreen,
const colorsectors = Dict{Colorant,Char}(Red => SectorRed, Green => SectorGreen,
Blue => SectorBlue, Yellow => SectorYellow)
Blue => SectorBlue, Yellow => SectorYellow)
const colorballs = Dict{Color,Char}(Red => BallRed, Green => BallGreen,
const colorballs = Dict{Colorant,Char}(Red => BallRed, Green => BallGreen,
Blue => BallBlue, Yellow => BallYellow)
Blue => BallBlue, Yellow => BallYellow)


const configs = Dict{String,Any}("dimensions" => (8, 12), "ballchance" => 0.75,
const configs = Dict{String,Any}("dimensions" => (8, 12), "ballchance" => 0.67,
"wallchance" => 0.10, "walltile" => '▒', "emptytile" => ' ',
"wallchance" => 0.10, "walltile" => '▒', "emptytile" => ' ',
"balltile" => '\u25cd', "agenttile" => '\u2603', "agentwithball" => '\u26f9',
"unknowntile" => '?', "ip" => ip"127.0.0.1","portnumber" => 5021,
"unknowntile" => '?', "ip" => ip"127.0.0.1","portnumber" => 5021,
"wallcolor" => colorant"darkgray", "unknowncolor" => colorant"gold")
"wallcolor" => colorant"darkgray", "unknowncolor" => colorant"gold")
Line 60: Line 59:
mutable struct Sector
mutable struct Sector
ch::Char
ch::Char
clr::Color
clr::Colorant
ball::Color
ball::Colorant
end
end


Line 73: Line 72:
location::Pt
location::Pt
direction::Direction
direction::Direction
ball:: Color
ball::Colorant
end
end


Line 248: Line 247:




# modulino for testing
# modulino
if occursin(Base.PROGRAM_FILE, @__FILE__)
if occursin(Base.PROGRAM_FILE, @__FILE__)


Line 283: Line 282:
newgame()
newgame()


end # modulino for testing
end # modulino
</lang>
</lang>
Second file: gtk_remoteserversimulation.jl
File: gtk_remoteserversimulation.jl
<lang julia>using Distributed, Sockets, Gtk, Colors, Graphics, Cairo
<lang julia>using Distributed, Sockets, Gtk, Colors, Graphics, Cairo


Line 297: Line 296:
# want the map as learned by agent to be aligned with the map known to server.
# want the map as learned by agent to be aligned with the map known to server.
# To do so, we start agent facin north, but in a random location with the map
# To do so, we start agent facin north, but in a random location with the map
# height and width randomly excahnged, so knowing facing north gives no net
# height and width randomly exchanged, so knowing facing north gives no net
# information about the shape and contents of the grid.
# information about the shape and contents of the grid.
height, width = configs["dimensions"]
height, width = configs["dimensions"]
Line 336: Line 335:
line_to(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 0.2)
line_to(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 0.2)
stroke(ctx)
stroke(ctx)
set_source(ctx, grid.agent.ball)
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)
circle(ctx, (i - 1) * l + fontsize * 1.25, (j - 1) * l + fontsize * 2, fontsize / 4)
fill(ctx)
fill(ctx)
Line 362: Line 361:
end
end
if GameOver in response
if GameOver in response
for ch in [GameOver, GameOver, GameOver, GameOver, GameOver, Stop]
put!(outchan, ch)
end
sleep(10)
close(inchan)
close(inchan)
close(outchan)
close(outchan)

Revision as of 07:54, 11 July 2019

This implementation uses the Gtk GUI library. There are two files: a module and a file to implement the GUI server's Gtk game view.
File: RemoteGameServerClient.jl <lang julia>module RemoteGameServerClient

using Sockets, Distributed, Colors

export Forward, TurnRight, TurnLeft, Get, Drop, GameOver, Stop, SectorRed,

SectorGreen, SectorYellow, SectorBlue, BallRed, BallGreen, BallYellow,
   BallBlue, Bump, SectorFull, AgentFull, NoSectorBall, NoAgentBall

export North, East, South, West, Red, Green, Yellow, Blue, nocolor, Direction

export Pt, configs, asyncserversocketIO, asyncclientsocketIO, makegrid, hasball,

   iswall, inside, Sector, Agent, Grid, directions, sectorcolors, colorsectors,
   colorballs, adjacent, responses, compasspoints

const North = [-1, 0] const East = [0, 1] const South = [1, 0] const West = [0, -1] const Direction = Vector{Int} const compasspoints = [North, East, South, West]

const Forward = '^' const TurnRight = '>' const TurnLeft = '<' const Get = '@' const Drop = '!' const GameOver = '+' const Stop = '.' const SectorRed = 'R' const SectorGreen = 'G' const SectorYellow = 'Y' const SectorBlue = 'B' const BallRed = 'r' const BallGreen = 'g' const BallYellow = 'y' const BallBlue = 'b' const Bump = '|' const SectorFull = 'S' const AgentFull = 'A' const NoSectorBall = 's' const NoAgentBall = 'a'

const Red, Green, Blue, Yellow, nocolor = colorant"red", colorant"green",

   colorant"blue", colorant"yellow", colorant"transparent"

const sectorcolors = [Red, Green, Blue, Yellow] const colorsectors = Dict{Colorant,Char}(Red => SectorRed, Green => SectorGreen,

   Blue => SectorBlue, Yellow => SectorYellow)

const colorballs = Dict{Colorant,Char}(Red => BallRed, Green => BallGreen,

   Blue => BallBlue, Yellow => BallYellow)

const configs = Dict{String,Any}("dimensions" => (8, 12), "ballchance" => 0.67,

   "wallchance" => 0.10, "walltile" => '▒', "emptytile" => ' ',
   "unknowntile" => '?', "ip" => ip"127.0.0.1","portnumber" => 5021,
   "wallcolor" => colorant"darkgray", "unknowncolor" => colorant"gold")

mutable struct Sector

   ch::Char
   clr::Colorant
   ball::Colorant

end

struct Pt

   x::Int
   y::Int

end Pt(v::Vector{Int}) = new(v[1], v[2])

mutable struct Agent

   location::Pt
   direction::Direction
   ball::Colorant

end

mutable struct Grid

   mat::Matrix{Sector}
   agent::Agent
   turncount::Int64

end

hasball(obj) = obj.ball in sectorcolors inside(m::Matrix{Sector}, x::Int, y::Int) = (0 < x <= size(m)[1]) && (0 < y <= size(m)[2]) inside(m::Matrix{Sector}, p::Pt) = inside(m, p.y, p.y) adjacent(x, y) = [[x - 1, y], [x, y + 1], [x + 1, y], [x, y - 1]] adjacent(p::Pt) = [Pt(a[1], a[2]) for a in adjacent(p.x, p.y)] iswall(s::Sector) = s.ch == configs["walltile"] iswall(m::Matrix{Sector}, x, y) = inside(m, x, y) && iswall(m[x, y]) iswall(m, p::Pt) = iswall(m, p.x, p.y)

allballsmatch(grid) = all(x -> iswall(x) || !hasball(x) || x.clr == x.ball, grid.mat)

function makegrid(height, width)

   wallcolor = configs["wallcolor"]
   m = Matrix{Sector}(undef, height, width)
   probinteriorwall = configs["wallchance"]
   for i in 1:height, j in 1:width
       m[i, j] = ((i == 1) || (j == 1) || (i == height) || (j == width)) ?
           Sector(configs["walltile"], wallcolor, nocolor) :
       m[i, j] = probinteriorwall > rand() ?
           Sector(Char(configs["walltile"]), wallcolor, nocolor) :
           configs["ballchance"] > rand() ?
               Sector(Char(configs["emptytile"]), rand(sectorcolors, 2)...) :
               Sector(Char(configs["emptytile"]), rand(sectorcolors), nocolor)
   end
   for i in 2:height-1, j in 2:width-1 # once walls up, open any isolated sectors
       arr = adjacent(Pt(i, j))
       if !iswall(m, i, j) && all(p -> iswall(m, p), arr)
           m[i, j] = Sector(Char(configs["emptytile"]), rand(sectorcolors, 2)...)
       end
   end
   rows, cols = collect(2:height - 1), collect(2:width-1)
   while true
       i, j = rand(rows), rand(cols)
       if hasball(m[i, j])
           m[i, j] = Sector(configs["emptytile"], m[i, j].clr, nocolor)
           return Grid(m, Agent(Pt(i, j), North, nocolor), 0)
       end
   end

end

function forward!(grid)

   reply = Char[]
   px, py = grid.agent.location.x, grid.agent.location.y
   newx, newy = px + grid.agent.direction[1], py + grid.agent.direction[2]
   if iswall(grid.mat, newx, newy)
       push!(reply, Bump)
   else
       grid.agent.location = Pt(newx, newy)
       sect = grid.mat[newx, newy]
       push!(reply, colorsectors[sect.clr])
       if hasball(sect)
           push!(reply, colorballs[sect.ball])
       end
       grid.turncount += 1
   end
   push!(reply, Stop)
   reply

end

const rightdelta = Dict(North => East, East => South, South => West, West => North) const leftdelta = Dict(p[2] => p[1] for p in rightdelta) rightturn!(grid) = begin grid.agent.direction = rightdelta[grid.agent.direction]; [Stop] end leftturn!(grid) = begin grid.agent.direction = leftdelta[grid.agent.direction]; [Stop] end

function getball!(grid)

   reply = Char[]
   grid.turncount += 1
   if hasball(grid.agent)
       push!(reply, AgentFull)
   else
       p = grid.agent.location
       sect = grid.mat[p.x, p.y]
       if hasball(sect)
           grid.agent.ball = sect.ball
           sect.ball = nocolor
       else
           push!(reply, NoSectorBall)
       end
   end
   push!(reply, Stop)
   reply

end

function dropball!(grid)

   reply = Char[]
   grid.turncount += 1
   if !hasball(grid.agent)
       push!(reply, NoAgentBall)
   else
       p = grid.agent.location
       sect = grid.mat[p.x, p.y]
       if !hasball(sect)
           sect.ball = grid.agent.ball
           grid.agent.ball = nocolor
           if allballsmatch(grid)
               push!(reply, GameOver)
           end
       else
           push!(reply, SectorFull)
       end
   end
   push!(reply, Stop)
   reply

end

responses = Dict{Char,Function}(Forward => forward!, TurnRight => rightturn!,

   TurnLeft => leftturn!, Get => getball!, Drop => dropball!)

function asyncserversocketIO(inchan, outchan, debug=false)

   try
       socket = TCPSocket()
       server = listen(configs["ip"], configs["portnumber"])
       debug && println("Listening at ", configs["ip"], " ", configs["portnumber"])
       socket = accept(server)
       debug && println("Connected at ", configs["ip"], " ", configs["portnumber"])
       while isopen(socket) && isopen(inchan) && isopen(outchan)
           ch = read(socket, Char)
           put!(inchan, ch) 
           debug && println("socket sent to inchan: $ch")
           yield()
           while (ch = take!(outchan)) != '.'
               write(socket, ch)
               flush(socket)
               debug && println("outsocket: $ch")
           end
           write(socket, ch)
           flush(socket)
           debug && println("outsocket: $ch")
       end
   catch y
       @warn("Socket IO: caught exception $y")
   end

end

function asyncclientsocketIO(inchan, outchan, debug=false)

   try
       debug && println("Trying connection at ", configs["ip"], " ", configs["portnumber"])
       socket = connect(configs["ip"], configs["portnumber"])
       debug && println("Connected at ", configs["ip"], " ", configs["portnumber"])
       while isopen(socket) && isopen(inchan) && isopen(outchan)
           if !isready(outchan)
               sleep(0.05)
               yield()
           else
               while isready(outchan)
                   ch = take!(outchan)
                   debug && println("outsocket: $ch")
                   write(socket, ch)
                   flush(socket)
               end
               while (ch = read(socket, Char)) != '.'
                   put!(inchan, ch)
                   debug && println("inchan: $ch")
               end
               put!(inchan, ch)
               debug && println("inchan: $ch")             
           end
       end
   catch y
       println("Socket IO: caught exception $y")
   end

end

end # module RemoteGameServerClient


  1. modulino

if occursin(Base.PROGRAM_FILE, @__FILE__)

using .RemoteGameServerClient

function evalcommand(grid, inchan, outchan, debug=false)

   debug && println("Starting command evaluation service.")
   debug && println("Configuration settings are: $(RemoteGameServerClient.configs).")
   while isopen(inchan) && isopen(outchan)
       ch = take!(inchan)
       debug && println("responding to $ch as $(channelcodes[ch])")
       response = responses[channelcodes[ch]](grid)
       debug && println(" and $response")
       debug && println("Got code: $ch.  Response is: $response")
       for code in response
           ch = streamcodes[code]
           put!(outchan, ch)
       end
       if GameOver in response
           close(inchan)
           close(outchan)
           break
       end
   end

end

function newgame()

   serverin, serverout = Channel{Char}(100), Channel{Char}(100)
   @async asyncserversocketIO(serverin, serverout)
   grid = makegrid(10, 16)
   evalcommand(grid, serverin, serverout, true)

end

newgame()

end # modulino </lang> File: gtk_remoteserversimulation.jl <lang julia>using Distributed, Sockets, Gtk, Colors, Graphics, Cairo

include("RemoteGameServerClient.jl")

using .RemoteGameServerClient

function matchballsgameserver()

   # The player is not to know what direction they are facing, so as to not
   # give them more information about the map at start. However, we ideally would
   # want the map as learned by agent to be aligned with the map known to server.
   # To do so, we start agent facin north, but in a random location with the map
   # height and width randomly exchanged, so knowing facing north gives no net
   # information about the shape and contents of the grid.
   height, width = configs["dimensions"]
   if rand() > 0.5
       height, width = width, height
   end
   fontsize = configs["dimensions"][1] * 2
   win = GtkWindow("Match Color Balls Game      ", 600, 600) |> (GtkFrame() |> (box = GtkBox(:v)))
   can = GtkCanvas()
   set_gtk_property!(can, :expand, true)
   push!(box, can)
   grid = makegrid(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_font_size(ctx, fontsize)
       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]
           fill(ctx)
           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
       end
   end
   function evalcommand(grid, inchan, outchan, debug=false)
       debug && println("Starting command evaluation service.")
       debug && println("Configuration settings are: $(RemoteGameServerClient.configs).")
       while isopen(inchan) && isopen(outchan)
           ch = take!(inchan)
           debug && println("responding to $ch input from client")
           response = responses[ch](grid)
           debug && println(" and response is then $response")
           draw(can)
           show(can)
           show(win)
           debug && println("screen update done")
           for ch in response
               put!(outchan, ch)
           end
           if GameOver in response
               for ch in [GameOver, GameOver, GameOver, GameOver, GameOver, Stop]
                   put!(outchan, ch)
               end
               sleep(10)
               close(inchan)
               close(outchan)
               break
           end
       end
   end
   
   serverin, serverout = Channel{Char}(100), Channel{Char}(100)
   @async asyncserversocketIO(serverin, serverout, false)
   @async evalcommand(grid, serverin, serverout, false)
   
   condition = Condition()
   endit(w) = notify(condition)
   signal_connect(endit, win, :destroy)
   showall(win)
   wait(condition)

end

matchballsgameserver() </lang>