⚠️ Warning: This is a draft ⚠️
This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.
If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.
using Gtk.ShortNames, Colors, Cairo, Graphics const fontpointsize = 10 const mapwidth = 1000 const mapheight = 500 const windowmaxx = div(mapwidth, Int(round(fontpointsize * 0.92))) const windowmaxy = div(mapheight, fontpointsize) const basebuffer = fill(' ', windowmaxy, windowmaxx) win = Window("Boids", mapwidth, mapheight) |> (can = Canvas()) set_gtk_property!(can, :expand, true) @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, 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(basebuffer)[2] workbuf = Char[] for i in 1:size(basebuffer)[1] move_to(ctx, 0, i * fontpointsize) lastcharprinted = '\x01' for j in 1:linelen ch = basebuffer[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 @enum Directions NW N NE E SE S SW W Here const defaultdirection = E boidmoves = Dict{Directions, Vector{Int}}(Here => [0, 0], NW => [-1, -1], N => [0, -1], NE => [1, -1], E => [1, 0], SE => [1, 1], S => [0, 1], SW => [-1, 1], W => [-1, 0]) struct Point x::Int y::Int end mutable struct Obstacle occupied::Vector{Point} end mutable struct Walls occupied::Vector{Point} end mutable struct Environment width::Int height::Int walls::Walls obstacles::Vector{Obstacle} buffer::Matrix{Char} end ebuf(e, p::Point) = e.buffer[p.x, p.y] mutable struct Boid pos::Point flock::Vector{Boid} end aschar(b::Boid) = 'o' aschar(o::Obstacle) = '*' aschar(w::Walls) = '\u2593' function buildwalls(environ) for i in 1:environ.height push!(environ.walls.occupied, Point(1, i), Point(environ.width, i)) end for i in 1:environ.width push!(environ.walls.occupied, Point(i, 1), Point(i, environ.height)) end for p in environ.walls.occupied if 0 < p.x <= environ.width && 0 < p.y <= environ.height environ.buffer[p.y, p.x] = aschar(environ.walls) end end end inellipse(dx, dy, a, b) = (dx / a)^2 + (dy / b)^2 < 1.0 inellipseat(p, x, y, a, b) = inellipse(x - p.x, y - p.y, a, b) function buildobstacles(environ, n=5) widthoptions = collect(3:max(5, div(environ.height, 10))) heightoptions = collect(7:max(10, div(environ.height, 2))) for i in 1:n obst = Obstacle(Point[]) push!(environ.obstacles, obst) w, h = rand(widthoptions), rand(heightoptions) w, h = (w > h) ? (h, w) : (w, h) center = Point(Int(round(environ.width * rand())), Int(round(environ.height * rand()))) for y in center.y-h:center.y+h, x in center.x-w:center.x+w if inellipseat(center, x, y, w, h) && 1 < x < environ.width && 1 < y < environ.height push!(obst.occupied, Point(x, y)) environ.buffer[y, x] = aschar(obst) end end end end function buildenvironment() environ = Environment(windowmaxx, windowmaxy, Walls(Point[]), Obstacle[], basebuffer) buildwalls(environ) buildobstacles(environ) environ end function addflock(allflocks, numboids, environ) f = Vector{Boid}() center = Point(12, div(environ.height, 2)) varpick = collect(-10:10) while length(f) < numboids while true newboid = Boid(Point(center.x + rand(varpick), center.y + rand(varpick)), f) if all(x -> x.pos != newboid.pos, f) && environ.buffer[newboid.pos.y, newboid.pos.x] == ' ' push!(f, newboid) break end end end push!(allflocks, f) end function availablemoves(boid, environ) avail = Vector{Directions}() for (direc, v) in boidmoves if environ.buffer[boid.pos.y + v[2], boid.pos.x + v[1]] == ' ' push!(avail, direc) end end avail end function center(flock) xs, ys, n = 0, 0, length(flock) for b in flock xs += b.pos.x ys += b.pos.y end Point(Int(round(xs / n)), Int(round(ys / n))) end isobstacle(x, y, environ) = (c = environ.buffer[y, x]; c != '*' && c != '\u2593') nextobstaclex(pos, e) = (x = pos.x + 1; while e.buffer[pos.y, x] == ' ' x += 1 end; x) nearobs(b, e, delt=8) = b.pos.x - nextobstaclex(b.pos, e) < delt atobs(b, e) = e.buffer[b.pos.y, b.pos.x + 1] != ' ' function nearbyopen(b, e, opendist = e.width) dist, y = findmax([nextobstaclex(Point(b.pos.x, y), e) for y in 1:e.height]) return y end function move(boid, d::Directions) m = boidmoves[d] boid.pos = Point(boid.pos.x + m[1], boid.pos.y + m[2]) end showboid(b, environ) = begin x, y = b.pos.x, b.pos.y; environ.buffer[y, x] = aschar(b) end hideboid(b, environ) = begin x, y = b.pos.x, b.pos.y; environ.buffer[y, x] = ' ' end showmove(b, e) = begin hideboid(b, e); move(b, e); showboid(b, e) end function move(boid, environ::Environment) possmoves = availablemoves(boid, environ) fcenter = center(boid.flock) wantsouth, wantnorth = false, false if atobs(boid, environ) wanty = nearbyopen(boid, environ, 8) d = wanty > boid.pos.y && S in possmoves ? S : N in possmoves ? N : rand(possmoves) move(boid, d) return end if rand() > 0.5 && nearobs(boid, environ) wanty = nearbyopen(boid, environ, 8) d = wanty > boid.pos.y && SE in possmoves ? SE : NE in possmoves ? NE : E move(boid, d) return end if rand() > 0.5 && fcenter.x < boid.pos.x - 2 && W in possmoves move(boid, W) return end if fcenter.y > boid.pos.y + 1 wantsouth = true elseif fcenter.y < boid.pos.y - 1 wantnorth = true end if wantsouth if SE in possmoves move(boid, SE) elseif S in possmoves move(boid, S) end elseif wantnorth if NE in possmoves move(boid, NE) elseif N in possmoves move(boid, N) end elseif E in possmoves move(boid, E) end end const itemcolors = Dict{Char, Colorant}('o' => colorant"white", ' ' => colorant"black", '*' => colorant"gold", '\u2593' => colorant"silver") environ = buildenvironment() const allflocks = Vector{Vector{Boid}}() addflock(allflocks, 5, environ) draw(can) show(can) while true sleep(0.5) for flock in allflocks, boid in flock showmove(boid, environ) end draw(can) end