Elevator simulation
To see how it works watch the next video
Elevator Simulation in Ring - video
Necessary image files - download
Please if you can attach a video file how your sample works.
Elevator simulation is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
- Task
#include "fbgfx.bi"
' Constants
Const WINDOW_WIDTH = 800
Const WINDOW_HEIGHT = 700
Const CD_DARKER_GREY = &h383838
Const CD_LIGHTER_ORANGE = &hFFA500
Const CD_PALE_BLUE = &h98C5DA
Const CD_MEDIUM_BLUE = &h759EB4
Type Button
x As Integer
y As Integer
text As String
Declare Constructor ()
Declare Constructor (x As Integer, y As Integer, txt As String)
End Type
Constructor Button()
x = 0
y = 0
text = ""
End Constructor
Constructor Button(x As Integer, y As Integer, txt As String)
This.x = x
This.y = y
This.text = txt
End Constructor
Type Panel
x As Integer
y As Integer
ancho As Integer
alto As Integer
Declare Constructor ()
Declare Constructor (x As Integer, y As Integer, w As Integer, h As Integer)
End Type
Constructor Panel()
x = 0
y = 0
ancho = 0
alto = 0
End Constructor
Constructor Panel(x As Integer, y As Integer, w As Integer, h As Integer)
This.x = x
This.y = y
This.ancho = w
This.alto = h
End Constructor
' Variables
Dim Shared As Integer curFloor, waitSign, actionCount
Dim Shared As Boolean doorOpen
Dim Shared As String actions(100)
' Initialize panels
Dim Shared panels(4) As Panel
panels(0) = Panel(195, 30, 45, 12) ' indicator
panels(1) = Panel(380, 150, 50, 90) ' up/down
panels(2) = Panel(380, 465, 100, 210) ' main
panels(3) = Panel(625, 330, 75, 25) ' restart
panels(4) = Panel(625, 530, 75, 25) ' exit
' Initialize buttons
Dim Shared buttons(9) As Button
buttons(0) = Button(380, 110, "^")
buttons(1) = Button(380, 190, "v")
buttons(2) = Button(335, 305, "5")
buttons(3) = Button(420, 305, "6")
buttons(4) = Button(335, 385, "3")
buttons(5) = Button(420, 385, "4")
buttons(6) = Button(335, 465, "1")
buttons(7) = Button(420, 465, "2")
buttons(8) = Button(380, 540, "GF")
buttons(9) = Button(380, 630, "><")
Sub NewGame()
curFloor = Int(Rnd * 7)
waitSign = Int(Rnd * 7)
doorOpen = (Int(Rnd * 2) = 1)
actionCount = 0
End Sub
Sub DrawElevator()
Dim As Integer i, x, y
Screenlock
Cls
' Draw panels
For i = 0 To 4
Line (panels(i).x - panels(i).ancho, panels(i).y - panels(i).alto)- _
(panels(i).x + panels(i).ancho, panels(i).y + panels(i).alto), _
CD_LIGHTER_ORANGE, BF
Next i
' Draw floor indicator
Draw String (180, 20), Iif(curFloor = 0, "GF", Str(curFloor)), &hFFFFFF
' Draw elevator shaft
Dim As Integer floorY = 50
For i = 6 To 0 Step -1
' Draw floor background
Line (155, floorY)-(235, floorY + 80), &hFFFFFF, BF
Line (155, floorY)-(235, floorY + 80), &h808080, B
' Draw floor number with better positioning
Draw String (130, floorY + 35), Iif(i = 0, "GF", Str(i)), &hFFFFFF
If i = curFloor Then
' Draw elevator car with better visibility
Line (160, floorY + 5)-(230, floorY + 75), CD_MEDIUM_BLUE, BF
If doorOpen Then
' Draw open doors
Line (165, floorY + 5)-(185, floorY + 75), CD_PALE_BLUE, BF
Line (205, floorY + 5)-(225, floorY + 75), CD_PALE_BLUE, BF
Else
' Draw closed doors
Line (160, floorY + 5)-(230, floorY + 75), CD_MEDIUM_BLUE, BF
End If
End If
' The WAIT sign indicates a floor where the elevator needs to stop
If i = waitSign Then
Circle (195, floorY + 40), 25, &hFFFF00,, , , F
Draw String (185, floorY + 35), "WAIT", &h000000
End If
floorY += 90
Next i
' Draw buttons
For i = 0 To 9
Circle (buttons(i).x, buttons(i).y), 30, &hC0C0C0,, , , F
Circle (buttons(i).x, buttons(i).y), 30, &h000000
Draw String (buttons(i).x - 5, buttons(i).y - 5), buttons(i).text, &h000000
Next i
' Draw panel labels
Draw String (panels(3).x - 20, panels(3).y - 5), "Restart", &hFFFFFF
Draw String (panels(4).x - 15, panels(4).y - 5), "Exit", &hFFFFFF
Screenunlock
End Sub
Sub ProcessActions()
If actionCount > 0 Then
Dim action As String = actions(0)
' Shift array left
For i As Integer = 0 To actionCount - 2
actions(i) = actions(i + 1)
Next i
actionCount -= 1
' Regular floor movement processing
Select Case action
Case "GF"
If curFloor > 0 Then
curFloor -= 1
doorOpen = False
Else
doorOpen = True
End If
Case "1" To "6"
Dim As Integer targetFloor = Valint(action)
' Add multiple movements until target floor is reached
While curFloor <> targetFloor
If curFloor < targetFloor Then
curFloor += 1
Else
curFloor -= 1
End If
DrawElevator()
Sleep 500
Wend
doorOpen = True
Case "^"
If curFloor < 6 Then
curFloor += 1
doorOpen = False
End If
Case "v"
If curFloor > 0 Then
curFloor -= 1
doorOpen = False
End If
Case "><"
doorOpen = Not doorOpen
End Select
' Check for WAIT sign
If curFloor = waitSign Then
waitSign = -1
doorOpen = True
actions(actionCount) = "><"
actionCount += 1
End If
DrawElevator()
End If
End Sub
Sub main()
Screenres WINDOW_WIDTH, WINDOW_HEIGHT, 32
Windowtitle "Elevator Simulation"
Randomize Timer
NewGame()
Dim As Integer mx, my, mb, i
Do
DrawElevator()
Getmouse mx, my, , mb
If mb And 1 Then
' Check button clicks
For i = 0 To 9
If (mx - buttons(i).x) ^ 2 + (my - buttons(i).y) ^ 2 <= 900 Then
actions(actionCount) = buttons(i).text
actionCount += 1
Sleep 100
Exit For
End If
Next i
' Check panel clicks
If mx >= panels(3).x - panels(3).ancho And mx <= panels(3).x + panels(3).ancho Then
If my >= panels(3).y - panels(3).alto And my <= panels(3).y + panels(3).alto Then
NewGame()
Elseif my >= panels(4).y - panels(4).alto And my <= panels(4).y + panels(4).alto Then
Exit Do
End If
End If
End If
ProcessActions()
Sleep 100
Loop Until Multikey(FB.SC_ESCAPE)
End Sub
main()
Uses the GTK 4.0 toolkit.
using Gtk4
using Cairo
using Colors
# Constants
const WINDOW_WIDTH = 800
const WINDOW_HEIGHT = 700
const CD_DARKER_GREY = RGB(56/255, 56/255, 56/255)
const CD_LIGHTER_ORANGE = RGB(255/255, 165/255, 0/255)
const CD_PALE_BLUE = RGB(152/255, 197/255, 218/255)
const CD_MEDIUM_BLUE = RGB(117/255, 158/255, 180/255)
""" Elevator Button """
mutable struct Button
x::Float64
y::Float64
text::String
radius::Float64
Button() = new(0.0, 0.0, "", 0.0)
Button(x::Real, y::Real, txt::String, r::Real) = new(Float64(x), Float64(y), txt, Float64(r))
end
""" Panel struct """
mutable struct Panel
x::Float64
y::Float64
width::Float64
height::Float64
Panel() = new(0.0, 0.0, 0.0, 0.0)
Panel(x::Real, y::Real, w::Real, h::Real) = new(Float64(x), Float64(y), Float64(w), Float64(h))
end
""" State variables """
mutable struct ElevatorState
curFloor::Int
waitSign::Int
doorOpen::Bool
actionCount::Int
actions::Vector{String}
drawing_function::Function
ElevatorState() = new(0, 0, false, 0, fill("", 100), () -> nothing)
end
# Global elevator state
const elevator_state = ElevatorState()
# Initialize panels and buttons (same coordinates as original)
const panels = [
Panel(195, 30, 45, 12), # indicator
Panel(380, 150, 50, 90), # up/down
Panel(380, 465, 100, 210), # main
Panel(625, 330, 75, 25), # restart
Panel(625, 530, 75, 25), # exit
]
const buttons = [
Button(380, 110, "^", 30),
Button(380, 190, "v", 30),
Button(335, 305, "5", 30),
Button(420, 305, "6", 30),
Button(335, 385, "3", 30),
Button(420, 385, "4", 30),
Button(335, 465, "1", 30),
Button(420, 465, "2", 30),
Button(380, 540, "GF", 30),
Button(380, 630, "><", 30),
]
""" Reset the game state by randomizing the current floor, wait sign, and door status. """
function newGame!()
elevator_state.curFloor = rand(0:6)
elevator_state.waitSign = rand(0:6)
elevator_state.doorOpen = rand(Bool)
elevator_state.actionCount = 0
fill!(elevator_state.actions, "")
end
""" Helper function to set Cairo context color from Colors.jl RGB type. """
function set_cairo_color(ctx, color::RGB)
set_source_rgb(ctx, color.r, color.g, color.b)
end
""" Helper function to draw a filled rectangle with optional setting of color. """
function draw_rectangle(ctx, x, y, width, height, fill_color, stroke_color = nothing)
rectangle(ctx, x - width, y - height, width * 2, height * 2)
set_cairo_color(ctx, fill_color)
fill_preserve(ctx)
if stroke_color !== nothing
set_cairo_color(ctx, stroke_color)
set_line_width(ctx, 1.0)
end
stroke(ctx)
end
""" Helper function to draw a filled diamond with optional setting of color. """
function draw_diamond(ctx, x, y, width, height, fill_color, stroke_color = nothing)
move_to(ctx, x, y - height / 2)
line_to(ctx, x - width / 2, y)
line_to(ctx, x, y + height / 2)
line_to(ctx, x + width / 2, y)
close_path(ctx)
set_cairo_color(ctx, fill_color)
fill_preserve(ctx)
if stroke_color !== nothing
set_cairo_color(ctx, stroke_color)
set_line_width(ctx, 1.0)
end
stroke(ctx)
end
""" Helper function to draw a filled circle with optional setting of color. """
function draw_circle(ctx, x, y, radius, fill_color, stroke_color = nothing)
arc(ctx, x, y, radius, 0, 2π)
set_cairo_color(ctx, fill_color)
fill_preserve(ctx)
if stroke_color !== nothing
set_cairo_color(ctx, stroke_color)
set_line_width(ctx, 1.0)
end
stroke(ctx)
end
""" Helper function to draw text centered at given position. """
function draw_text(ctx, text, x, y, color, font_size = 24)
set_cairo_color(ctx, color)
select_font_face(ctx, "Arial", 0, 1)
set_font_size(ctx, font_size)
_, _, extents_width, extents_height = text_extents(ctx, text)
move_to(ctx, x - extents_width / 2, y - extents_height / 2)
show_text(ctx, text)
end
""" Main drawing function that renders the entire elevator scene. """
function draw_elevator_scene(ctx)
# Set background
set_cairo_color(ctx, CD_DARKER_GREY)
paint(ctx)
# Draw panels
for panel in panels
draw_rectangle(ctx, panel.x, panel.y, panel.width, panel.height,
CD_LIGHTER_ORANGE, RGB(0, 0, 0))
end
# Draw panel labels
draw_text(ctx, "Restart", panels[4].x, panels[4].y + 12, RGB(0, 0, 0), 16)
draw_text(ctx, "Exit", panels[5].x, panels[5].y + 12, RGB(0, 0, 0), 16)
# Floor indicator
floor_text = elevator_state.curFloor == 0 ? "GF" : string(elevator_state.curFloor)
draw_text(ctx, floor_text, panels[1].x, panels[1].y + 11, RGB(0, 0, 0), 16)
# Draw elevator shaft and floors
for i ∈ 6:-1:0
floorY = 50 + (6 - i) * 90
# Draw floor background
draw_rectangle(ctx, 195, floorY + 40, 40, 40, RGB(1, 1, 1), RGB(0.5, 0.5, 0.5))
# Draw floor number
floor_num = i == 0 ? "GF" : string(i)
draw_text(ctx, floor_num, 130, floorY + 35, RGB(1, 1, 1), 16)
end
# Elevator car position
elevator_y = 50 + (6 - elevator_state.curFloor) * 90 + 40
# Draw elevator car
draw_rectangle(ctx, 195, elevator_y, 35, 35, CD_MEDIUM_BLUE)
# Draw doors
door_color = elevator_state.doorOpen ? CD_PALE_BLUE : CD_MEDIUM_BLUE
draw_rectangle(ctx, 175, elevator_y, 10, 35, door_color)
draw_rectangle(ctx, 215, elevator_y, 10, 35, door_color)
# Draw wait sign
if elevator_state.waitSign >= 0
wait_y = 50 + (6 - elevator_state.waitSign) * 90 + 40
draw_diamond(ctx, 193, wait_y, 75, 75, RGB(1, 1, 0), RGB(0, 0, 0))
set_line_width(ctx, 2.0)
draw_text(ctx, "WAIT", 194, wait_y + 14, RGB(0, 0, 0), 16)
stroke(ctx)
end
# Draw buttons
for button in buttons
draw_circle(ctx, button.x, button.y, button.radius,
RGB(0.75, 0.75, 0.75), RGB(0, 0, 0))
stroke(ctx)
draw_text(ctx, button.text, button.x - 2, button.y + (length(button.text) > 1 ? 25 : 35), RGB(0, 0, 0), length(button.text) > 1 ? 35 : 50)
stroke(ctx)
end
end
""" Processes actions from the global actions queue and updates elevator state. """
function processActions!()
if elevator_state.actionCount > 0
action = elevator_state.actions[1]
elevator_state.actions[1:(end-1)] = elevator_state.actions[2:end]
elevator_state.actions[end] = ""
elevator_state.actionCount -= 1
if action == "GF"
if elevator_state.curFloor > 0
elevator_state.curFloor -= 1
elevator_state.doorOpen = false
else
elevator_state.doorOpen = true
end
elseif action in ["1", "2", "3", "4", "5", "6"]
target_floor = parse(Int, action)
# Simulate movement floor by floor
while elevator_state.curFloor != target_floor
if elevator_state.curFloor < target_floor
elevator_state.curFloor += 1
else
elevator_state.curFloor -= 1
end
elevator_state.doorOpen = false
elevator_state.drawing_function()
sleep(0.5)
# Check for wait sign
if elevator_state.curFloor == elevator_state.waitSign
elevator_state.waitSign = -1
elevator_state.doorOpen = true
elevator_state.drawing_function()
sleep(1.5)
elevator_state.doorOpen = false
elevator_state.drawing_function()
sleep(0.5)
end
end
elevator_state.doorOpen = true
elseif action == "^"
if elevator_state.curFloor < 6
elevator_state.curFloor += 1
elevator_state.doorOpen = false
end
elseif action == "v"
if elevator_state.curFloor > 0
elevator_state.curFloor -= 1
elevator_state.doorOpen = false
end
elseif action == "><"
elevator_state.doorOpen = !elevator_state.doorOpen
sleep(1)
end
end
end
""" Handle mouse clicks on buttons and panels. """
function handle_button_click(x, y)
# Check for button clicks
for button in buttons
dist_sq = (x - button.x)^2 + (y - button.y)^2
if dist_sq <= button.radius^2
elevator_state.actions[elevator_state.actionCount+1] = button.text
elevator_state.actionCount += 1
return true
end
end
# Check for panel clicks (Restart/Exit)
if panels[4].x - panels[4].width <= x <= panels[4].x + panels[4].width &&
panels[4].y - panels[4].height <= y <= panels[4].y + panels[4].height
newGame!()
return true
elseif panels[5].x - panels[5].width <= x <= panels[5].x + panels[5].width &&
panels[5].y - panels[5].height <= y <= panels[5].y + panels[5].height
exit()
end
return false
end
""" Set up and run Gtk4 window and canvas. """
function main()
# Create application
app = GtkWindow("Elevator Simulation", WINDOW_WIDTH, WINDOW_HEIGHT)
canvas = GtkCanvas(WINDOW_WIDTH, WINDOW_HEIGHT)
app[] = canvas
@guarded draw(canvas) do widget
ctx = getgc(canvas)
draw_elevator_scene(ctx)
end
elevator_state.drawing_function = () -> draw(canvas)
# Set up mouse click handling
gesture = GtkGestureClick(canvas)
signal_connect(gesture, "pressed") do gesture, n_press, x, y
handle_button_click(x, y)
end
# Set up key press handling
key_controller = GtkEventControllerKey()
signal_connect(key_controller, "key-pressed") do controller, keyval, keycode, state
if keyval == Gdk.KEY_Escape
close(window)
return true
end
return false
end
running = true
signal_connect(app, "destroy") do widget
running = false
end
newGame!()
show(app)
while running
processActions!()
elevator_state.drawing_function()
sleep(0.1)
end
end
# Run the application
main()
You can run this online here.
-- -- demo\rosetta\ElevatorSimulation.exw -- =================================== -- -- Even dumber than a real lift - clicking 16161616 ties it up for ages... -- with javascript_semantics include pGUI.e Ihandle dlg, canvas, timer cdCanvas cdcanvas constant title = "Elevator Simulation", CD_DARKER_GREY = #383838, -- (darker than CD_DARK_GREY) CD_LIGHTER_ORANGE = #FFA500, -- (lighter than CD_ORANGE) CD_PALE_BLUE = #98C5DA, -- (lighter than CD_LIGHT_BLUE) CD_MEDIUM_BLUE = #759EB4, -- (darker than CD_LIGHT_BLUE) panels = {{195,30,45,12}, -- indicator {380,150,50,90}, -- up/down {380,465,100,210}, -- main {625,330,75,25}, -- restart {625,530,75,25}}, -- exit buttons = {{380,110,"^"}, {380,190,"v"}, {335,305,"5"},{420,305,"6"}, {335,385,"3"},{420,385,"4"}, {335,465,"1"},{420,465,"2"}, {380,540,"GF"}, {380,630,">|<"}} integer cur_floor, -- 0..6 wait_sign -- "", -1 if none bool door_open sequence actions = {} -- (see timer_cb) procedure new_game() cur_floor = rand(7)-1 wait_sign = rand(7)-1 door_open = rand(2)==1 end procedure function redraw_cb(Ihandle /*ih*/, integer /*posx*/, /*posy*/) integer {width, height} = IupGetIntInt(canvas, "DRAWSIZE") cdCanvasActivate(cdcanvas) cdCanvasSetBackground(cdcanvas, CD_DARKER_GREY) cdCanvasClear(cdcanvas) cdCanvasSetForeground(cdcanvas, CD_LIGHTER_ORANGE) cdCanvasSetInteriorStyle(cdcanvas, CD_SOLID) for i=1 to length(panels) do integer {cx,cy,hw,hh} = panels[i] cdCanvasRoundedBox(cdcanvas,cx-hw,cx+hw,height-(cy+hh),height-(cy-hh),5,4) // (a filled box has no antialiasing, so overwrite with an outline box too:) cdCanvasRoundedRect(cdcanvas,cx-hw,cx+hw,height-(cy+hh),height-(cy-hh),5,4) end for cdCanvasSetForeground(cdcanvas, CD_BLACK) cdCanvasFont(cdcanvas, "Helvetica", CD_PLAIN, 16) {} = cdCanvasTextAlignment(cdcanvas,CD_CENTER) integer {cx,cy} = panels[1] string floor_text = iff(cur_floor=0?"GF":sprintf("%d",cur_floor)) cdCanvasText(cdcanvas,cx,height-cy,floor_text) cy += 75 for i=6 to 0 by -1 do integer minx = cx-40, maxx = cx+40, miny = height-(cy+45), maxy = height-(cy-40) cdCanvasSetForeground(cdcanvas, CD_PARCHMENT) cdCanvasBox(cdcanvas,minx,maxx,miny,maxy) if i=cur_floor then if i=wait_sign then wait_sign = -1 end if cdCanvasSetForeground(cdcanvas, CD_MEDIUM_BLUE) cdCanvasBox(cdcanvas,minx,maxx,miny,maxy) cdCanvasSetForeground(cdcanvas, CD_PALE_BLUE) cdCanvasBox(cdcanvas,minx+5,maxx-5,miny,maxy) if door_open then cdCanvasSetForeground(cdcanvas, CD_CYAN) cdCanvasBox(cdcanvas,minx+8,maxx-8,miny,maxy) else cdCanvasSetForeground(cdcanvas, CD_MEDIUM_BLUE) cdCanvasLine(cdcanvas,cx,miny,cx,maxy) end if elsif i=wait_sign then integer y3 = height-cy-3 for j=1 to 2 do cdCanvasSetForeground(cdcanvas, {CD_YELLOW,CD_BLACK}[j]) cdCanvasBegin(cdcanvas,{CD_FILL,CD_CLOSED_LINES}[j]) cdCanvasVertex(cdcanvas, cx, miny+3) cdCanvasVertex(cdcanvas, maxx-2, y3) cdCanvasVertex(cdcanvas, cx, maxy-4) cdCanvasVertex(cdcanvas, minx+1, y3) cdCanvasEnd(cdcanvas) end for cdCanvasText(cdcanvas,cx,y3,"WAIT") end if cy += 90 end for cdCanvasFont(cdcanvas, "Helvetica", CD_PLAIN, 20) for i=1 to length(buttons) do {cx,cy,floor_text} = buttons[i] cy = height-cy cdCanvasSetForeground(cdcanvas, CD_SILVER) cdCanvasSector(cdcanvas, cx, cy, 60, 60, 0, 360) cdCanvasSetForeground(cdcanvas, CD_BLACK) cdCanvasArc(cdcanvas, cx, cy, 50, 50, 0, 360) cdCanvasArc(cdcanvas, cx, cy, 60, 60, 0, 360) {} = cdCanvasTextOrientation(cdcanvas, iff(i=1?180:0)) cdCanvasText(cdcanvas,cx,cy,iff(i=1?"v":floor_text)) end for cdCanvasFont(cdcanvas, "Helvetica", CD_PLAIN, 16) cdCanvasSetForeground(cdcanvas, CD_BLACK) {cx,cy} = panels[4] cdCanvasText(cdcanvas,cx,height-cy,"Restart") {cx,cy} = panels[5] cdCanvasText(cdcanvas,cx,height-cy,"Exit") cdCanvasFlush(cdcanvas) return IUP_DEFAULT end function function map_cb(Ihandle ih) atom res = IupGetDouble(NULL, "SCREENDPI")/25.4 IupGLMakeCurrent(canvas) if platform()=JS then cdcanvas = cdCreateCanvas(CD_IUP, canvas) else cdcanvas = cdCreateCanvas(CD_GL, "798x698 %g", {res}) end if cdCanvasSetBackground(cdcanvas, CD_PARCHMENT) return IUP_DEFAULT end function function button_cb(Ihandle canvas, integer button, pressed, x, y, atom /*pStatus*/) if button=IUP_BUTTON1 and pressed then integer cx,cy,hw,hh string floor_text for i=1 to length(buttons) do {cx,cy,floor_text} = buttons[i] {cx,cy} = {cx-x,cy-y} if cx*cx+cy*cy<=3600 then -- (the button radius is 60) actions &= floor_text[1] -- (let timer_cb sort it out) IupSetInt(timer,"RUN",true) exit end if end for for i=4 to 5 do {cx,cy,hw,hh} = panels[i] if x>=cx-hw and x<=cx+hw and y>=cy-hh and y<=cy+hh then if i=5 then return IUP_CLOSE end if new_game() IupRedraw(dlg) exit end if end for end if return IUP_CONTINUE end function function timer_cb(Ihandle /*ih*/) if length(actions)=0 then IupSetInt(timer,"RUN",false) else integer action -- 'G'==='0', '<' is open, '>' is close. {action,actions} = {actions[1],actions[2..$]} switch action do case 'G': action = '0'; fallthrough case '0','1','2','3','4','5','6': integer d = action-'0'-cur_floor if d!=0 then actions = repeat(sign(d),abs(d)) & '<' & actions if door_open then actions = prepend(actions,'>') end if elsif not door_open then actions = prepend(actions,'<') end if case -1,+1: cur_floor += action case '>','<': door_open = action=='<' case '^','v': if wait_sign!=-1 and compare(cur_floor,wait_sign)==compare(action,'_') then -- (hint: in ascii, '^' < '_' whereas 'v' > '_' ... --- plus action ain't ever gonna be '_' here.) actions = prepend(actions,wait_sign+'0') end if default: ?9/0 -- unknown action? end switch IupRedraw(dlg) end if return IUP_IGNORE end function procedure main() IupOpen() canvas = IupGLCanvas("RASTERSIZE=800x700") sequence cb = {"MAP_CB", Icallback("map_cb"), "ACTION", Icallback("redraw_cb"), "BUTTON_CB", Icallback("button_cb")} IupSetCallbacks(canvas, cb) dlg = IupDialog(canvas,`TITLE="%s", RESIZE=NO`,{title}) new_game() IupShow(dlg) timer = IupTimer(Icallback("timer_cb"),500,false) if platform()!=JS then IupMainLoop() IupClose() end if end procedure main()
import pygame
import random
WIDTH, HEIGHT = 800, 600
GRAY = (125, 125, 125)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
YELLOW = (255, 255, 0)
ORANGE = (255, 175, 0)
LIGHT_BLUE = (0, 175, 255)
class Button:
def __init__(
self,
x: int,
y: int,
width: int,
height: int,
border_radius: float,
text: str,
bg_color: pygame.typing.ColorLike,
fg_color: pygame.typing.ColorLike,
font: pygame.Font,
on_click_val=None,
):
self.__btn_rect = pygame.Rect(x, y, width, height)
self.__border_radius = border_radius
self.__text = text
self.__bg_color = bg_color
self.__fg_color = fg_color
self.__on_click_val = on_click_val
self.__font = font
def draw(self, surface: pygame.Surface):
text_surf = self.__font.render(self.__text, False, self.__fg_color)
pygame.draw.rect(
surface,
self.__bg_color,
self.__btn_rect,
border_radius=int(self.__btn_rect.width * self.__border_radius),
)
text_rect = text_surf.get_rect(center=self.__btn_rect.center)
surface.blit(text_surf, text_rect)
def mouse_over(self):
return self.__btn_rect.collidepoint(pygame.mouse.get_pos())
def on_click(self):
return self.__on_click_val
class Floor:
def __init__(
self,
x: int,
y: int,
width: int,
height: int,
is_elevator: bool,
doors_open: bool,
is_wait: bool,
font: pygame.Font,
):
self.__rect = pygame.Rect(x, y, width, height)
self.__is_elevator = is_elevator
self.__doors_open = doors_open
self.__is_wait = is_wait
self.__font = font
def draw(self, surface: pygame.Surface):
pygame.draw.rect(surface, WHITE, self.__rect)
if not self.__is_elevator and self.__is_wait:
circle_rect = pygame.draw.circle(surface, YELLOW, self.__rect.center, 25)
text_surf = self.__font.render("WAIT", False, BLACK)
text_rect = text_surf.get_rect(center=circle_rect.center)
surface.blit(text_surf, text_rect)
elif self.__doors_open and self.__is_elevator:
third = self.__rect.width // 3
pygame.draw.rect(
surface,
LIGHT_BLUE,
pygame.Rect(
self.__rect.left, self.__rect.top, third, self.__rect.height
),
)
pygame.draw.rect(
surface,
LIGHT_BLUE,
pygame.Rect(
self.__rect.right - third,
self.__rect.top,
third,
self.__rect.height,
),
)
elif self.__is_elevator:
pygame.draw.rect(surface, LIGHT_BLUE, self.__rect)
def get_random_start_wait_floors():
floors = list(range(7))
wait_floor = random.choice(floors)
floors.remove(wait_floor)
start_floor = random.choice(floors)
return wait_floor, start_floor
def main():
pygame.init()
pygame.display.set_caption("Elevator simulation")
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
font = pygame.Font(None, size=17)
running = True
panel_x = 320
panel_y = 200
btn_width = 50
btn_height = 50
start_y_inc = 10
floor_btns = []
door_open = True
is_moving = False
reached_wait = False
for floor in range(6, 0, -2):
floor_btns.append(
Button(
panel_x + 30,
panel_y + start_y_inc,
btn_width,
btn_height,
0.5,
str(floor - 1),
GRAY,
BLACK,
font,
floor - 1,
)
)
floor_btns.append(
Button(
panel_x + btn_width + 40,
panel_y + start_y_inc,
btn_width,
btn_height,
0.5,
str(floor),
GRAY,
BLACK,
font,
floor,
)
)
if floor == 2:
floor_btns.append(
Button(
panel_x + btn_width + 10,
panel_y + (start_y_inc := start_y_inc + btn_height + 10),
btn_width,
btn_height,
0.5,
"GF",
GRAY,
BLACK,
font,
0,
)
)
start_y_inc += btn_height + 10
close_door_btn = Button(
panel_x + btn_width + 10,
panel_y + start_y_inc,
btn_width,
btn_height,
0.5,
"><",
GRAY,
BLACK,
font,
)
wait_floor, start_floor = get_random_start_wait_floors()
current_floor = start_floor
panel_rect = pygame.Rect(panel_x, panel_y, (btn_width // 2) * 7, 350)
upper_rect = pygame.Rect(
panel_x + ((btn_width // 2) * 7) // 2 - btn_width,
panel_y - (btn_height * 2) - 40,
btn_width * 2,
btn_height * 2 + 30,
)
up_btn = Button(
upper_rect.centerx - (btn_width // 2),
upper_rect.top + 10,
btn_width,
btn_height,
0.5,
"^",
GRAY,
BLACK,
font,
)
down_btn = Button(
upper_rect.centerx - (btn_width // 2),
upper_rect.top + btn_width + 20,
btn_width,
btn_height,
0.5,
"v",
GRAY,
BLACK,
font,
)
restart_btn = Button(
WIDTH - 150, 150, 100, btn_height, 0, "Restart", ORANGE, WHITE, font
)
exit_btn = Button(WIDTH - 150, 220, 100, btn_height, 0, "Exit", ORANGE, WHITE, font)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
for floor_btn in floor_btns:
if floor_btn.mouse_over():
start_floor = floor_btn.on_click()
is_moving = True
break
if close_door_btn.mouse_over():
door_open = not door_open
elif up_btn.mouse_over():
start_floor = 6
is_moving = True
elif down_btn.mouse_over():
start_floor = 0
is_moving = True
elif restart_btn.mouse_over():
wait_floor, start_floor = get_random_start_wait_floors()
current_floor = start_floor
reached_wait = False
is_moving = False
door_open = True
elif exit_btn.mouse_over():
running = False
screen.fill((0, 0, 0))
pygame.draw.rect(screen, ORANGE, panel_rect)
pygame.draw.rect(screen, ORANGE, upper_rect)
for btn in floor_btns:
btn.draw(screen)
up_btn.draw(screen)
down_btn.draw(screen)
close_door_btn.draw(screen)
restart_btn.draw(screen)
exit_btn.draw(screen)
floor_height = HEIGHT // 7
floor_height = (HEIGHT - floor_height) // 7
start_y = floor_height
indicator_rect = pygame.draw.rect(
screen,
ORANGE,
pygame.Rect(100, (floor_height // 2) - 10, 100, floor_height // 2),
)
indicator_text = font.render(
"GF" if current_floor == 0 else str(current_floor), False, WHITE
)
indicator_text_rect = indicator_text.get_rect(center=indicator_rect.center)
screen.blit(indicator_text, indicator_text_rect)
for i in range(6, -1, -1):
floor_text = font.render("GF" if i == 0 else str(i), False, WHITE)
screen.blit(
floor_text,
(100 - floor_text.width - 10, start_y + (floor_height - 10) // 2),
)
floor = Floor(
100,
start_y,
100,
floor_height - 10,
i == current_floor,
current_floor == start_floor and door_open,
i == wait_floor and current_floor != i and not reached_wait,
font,
)
floor.draw(screen)
start_y += floor_height
if is_moving:
if not reached_wait:
if (
current_floor < wait_floor
and current_floor < start_floor
and start_floor > wait_floor
):
start_floor = wait_floor
elif (
current_floor > wait_floor
and current_floor > start_floor
and start_floor < wait_floor
):
start_floor = wait_floor
if current_floor == start_floor:
if current_floor == wait_floor:
reached_wait = True
is_moving = False
door_open = True
elif current_floor > start_floor:
current_floor -= 1
elif current_floor < start_floor:
current_floor += 1
clock.tick(5)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
- Output:
Elevator Simulation in Ring - video
# Project : Elevator Game
# Date : 21/08/2021-02:26:57
# Author : Gal Zsolt (~ CalmoSoft ~)
# Email : calmosoft@gmail.com
load "stdlib.ring"
load "guilib.ring"
floorCount = 7
randEvelator = 1
randWait = 1
randOld = 0
nFloor = 0
count = 0
flag = 0
targetFloor = 0
floor = list(floorCount)
floorbtn = list(floorCount)
floorCall = list(floorCount)
C_UP = "images/up.jpg"
C_DOWN = "images/down.jpg"
C_WAIT = "images/wait.jpg"
C_EMPTY = "images/empty.jpg"
C_OPEN = "images/elevatoropen.jpg"
C_CLOSE = "images/elevatorclose.jpg"
C_CLOSEELEVATOR = "images/closeevelator.jpg"
C_RECTANGLE = "images/rectangle.jpg"
floorbtn[1] = "images/floor0.jpg"
floorbtn[2] = "images/floor1.jpg"
floorbtn[3] = "images/floor2.jpg"
floorbtn[4] = "images/floor3.jpg"
floorbtn[5] = "images/floor4.jpg"
floorbtn[6] = "images/floor5.jpg"
floorbtn[7] = "images/floor6.jpg"
oFont = new qfont("Verdana",16,0,0)
C_ButtonOrangeStyle = 'border-radius:6px;color:black; background-color: orange'
app = new qApp
{
stylefusionblack()
processevents()
win = new qWidget() {
setWindowTitle('Elevator Game')
setWindowIcon(new qIcon(new qPixmap(C_OPEN)))
move(550,140)
reSize(800,700)
app.processEvents()
for n = 1 to floorCount
floor[n] = new QPushButton(win)
{ setgeometry(150,510-(n-2)*90,85,85) }
next
showFloor = new QPushButton(win)
{ setgeometry(150,15,90,25)
setstylesheet(C_ButtonOrangestyle)
setfont(oFont)
}
rectangle = new QPushButton(win)
{ setgeometry(330,55,100,180)
setstylesheet(C_ButtonOrangestyle)
setIconSize(new qSize(66,66))
}
down = new QPushButton(win)
{ setgeometry(350,150,66,66)
setfont(oFont)
seticon(new qicon(new qpixmap(C_DOWN)))
setIconSize(new qSize(66,66))
setclickevent("pMoveDown()")
}
up = new QPushButton(win)
{ setgeometry(350,70,66,66)
setfont(oFont)
seticon(new qicon(new qpixmap(C_UP)))
setIconSize(new qSize(66,66))
setclickevent("pMoveUp()")
}
newGame = new QPushButton(win)
{ setgeometry(550,300,150,50)
setstylesheet(C_ButtonOrangestyle)
setfont(oFont)
settext("New Game")
setclickevent("pNewGame()")
}
exitButton = new QPushButton(win)
{ setgeometry(550,500,150,50)
setstylesheet(C_ButtonOrangestyle)
setfont(oFont)
setClickEvent("pExit()")
settext("Exit")
}
rectangleFloor = new QPushButton(win)
{ setgeometry(280,260,190,420)
setstylesheet(C_ButtonOrangestyle)
setIconSize(new qSize(66,66))
}
floorCall[1] = new QPushButton(win)
{ setgeometry(343,506,66,66)
seticon(new qicon(new qpixmap(floorbtn[1])))
setIconSize(new qSize(100,100))
settext("GF")
setclickevent("pMove(" + "1" + ")")
}
floorCall[2] = new QPushButton(win)
{ setgeometry(300,420,66,66)
seticon(new qicon(new qpixmap(floorbtn[2])))
setIconSize(new qSize(100,100))
setclickevent("pMove(" + "2" + ")")
}
floorCall[3] = new QPushButton(win)
{ setgeometry(386,420,66,66)
seticon(new qicon(new qpixmap(floorbtn[3])))
setIconSize(new qSize(100,100))
setclickevent("pMove(" + "3" + ")")
}
floorCall[4] = new QPushButton(win)
{ setgeometry(300,344,66,66)
seticon(new qicon(new qpixmap(floorbtn[4])))
setIconSize(new qSize(100,100))
setclickevent("pMove(" + "4" + ")")
show()
}
floorCall[5] = new QPushButton(win)
{ setgeometry(386,344,66,66)
seticon(new qicon(new qpixmap(floorbtn[5])))
setIconSize(new qSize(100,100))
setclickevent("pMove(" + "5" + ")")
show()
}
floorCall[6] = new QPushButton(win)
{ setgeometry(300,268,66,66)
seticon(new qicon(new qpixmap(floorbtn[6])))
setIconSize(new qSize(100,100))
setclickevent("pMove(" + "6" + ")")
}
floorCall[7] = new QPushButton(win)
{ setgeometry(386,268,66,66)
seticon(new qicon(new qpixmap(floorbtn[7])))
setIconSize(new qSize(100,100))
setclickevent("pMove(" + "7" + ")")
}
close = new QPushButton(win)
{ setgeometry(340,592,66,66)
seticon(new qicon(new qpixmap(C_CLOSEELEVATOR)))
setIconSize(new qSize(66,66))
setclickevent("pClose()")
}
show()
}
pBegin()
exec()
}
func pNewGame()
pBegin()
pWait()
func pBegin()
flag = 0
count = 0
for n = 1 to floorCount
floor[n] { seticon(new qicon(new qpixmap(C_EMPTY)))
setIconSize(new qSize(100,100)) }
next
randEvelator = random(floorCount-1) + 1
for n = 1 to floorCount
floorCall[n] { setenabled(False) }
next
up { setenabled(True) }
down { setenabled(True) }
pWait()
func pWait()
floor[randWait] { seticon(new qicon(new qpixmap(C_EMPTY)))
setIconSize(new qSize(100,100)) }
randWait = random(floorCount-1) + 1
while true
if randWait != randEvelator
nFloor = randEvelator
floor[randWait] { seticon(new qicon(new qpixmap(C_WAIT)))
setIconSize(new qSize(100,100)) }
floor[randEvelator] { seticon(new qicon(new qpixmap(C_CLOSE)))
setIconSize(new qSize(100,100)) }
showFloor.settext(string(randEvelator-1))
for n = 1 to floorCount
floorCall[n] { setenabled(False) }
next
exit
else
randEvelator = random(floorCount-1) + 1
ok
end
return
func pMove(sourceFloor)
if flag = 1
n = 0
pClose()
app.processEvents()
sleep(0.5)
up { setenabled(false) }
down { setenabled(false) }
count = count + 1
if count = 1
targetFloor = randWait
else
targetFloor = randOld
ok
n = 0
if sourceFloor > targetFloor
for n = targetFloor to sourceFloor - 1
app.processEvents()
sleep(0.5)
floor[n] { seticon(new qicon(new qpixmap(C_EMPTY)))
setIconSize(new qSize(100,100)) }
floor[n+1] { seticon(new qicon(new qpixmap(C_CLOSE)))
setIconSize(new qSize(100,100)) }
if n = 0
showFloor.settext("GF")
else
showFloor.settext(string(n))
ok
if n = sourceFloor - 1
nFloor = n + 1
floor[n+1] { seticon(new qicon(new qpixmap(C_OPEN)))
setIconSize(new qSize(100,100)) }
ok
next
see nl
ok
n = 0
if sourceFloor < targetFloor
for n = targetFloor to sourceFloor + 1 step -1
app.processEvents()
sleep(0.5)
floor[n] { seticon(new qicon(new qpixmap(C_EMPTY)))
setIconSize(new qSize(100,100)) }
floor[n-1] { seticon(new qicon(new qpixmap(C_CLOSE)))
setIconSize(new qSize(100,100)) }
if n = 2
showFloor.settext("GF")
else
showFloor.settext(string(n-2))
ok
if n = sourceFloor + 1
nFloor = n - 1
floor[n-1] { seticon(new qicon(new qpixmap(C_OPEN)))
setIconSize(new qSize(100,100)) }
ok
next
see nl
ok
randOld = sourceFloor
ok
func pMoveDown
if randWait > randEvelator
return
ok
for n = 1 to floorCount
floorCall[n] { setenabled(True) }
next
if randWait < randEvelator
for n = randEvelator to randWait + 1 step -1
app.processEvents()
sleep(0.5)
floor[n] { seticon(new qicon(new qpixmap(C_EMPTY)))
setIconSize(new qSize(100,100)) }
floor[n-1] { seticon(new qicon(new qpixmap(C_CLOSE)))
setIconSize(new qSize(100,100)) }
if n = 0
showFloor.settext("GF")
else
showFloor.settext(string(n-2))
ok
if n = randWait + 1
nFloor = n - 1
floor[n-1] { seticon(new qicon(new qpixmap(C_OPEN)))
setIconSize(new qSize(100,100)) }
ok
next
ok
app.processEvents()
sleep(1)
flag++
func pMoveUp()
if randWait < randEvelator
return
ok
for n = 1 to floorCount
floorCall[n] { setenabled(True) }
next
if randWait > randEvelator
for n = randEvelator to randWait - 1
app.processEvents()
sleep(0.5)
floor[n] { seticon(new qicon(new qpixmap(C_EMPTY)))
setIconSize(new qSize(100,100)) }
floor[n+1] { seticon(new qicon(new qpixmap(C_CLOSE)))
setIconSize(new qSize(100,100)) }
if n = 1
showFloor.settext("GF")
else
showFloor.settext(string(n))
ok
if n = randWait - 1
nFloor = n + 1
floor[n+1] { seticon(new qicon(new qpixmap(C_OPEN)))
setIconSize(new qSize(100,100)) }
ok
next
ok
app.processEvents()
sleep(1)
flag++
func pClose()
floor[nFloor] { seticon(new qicon(new qpixmap(C_CLOSE)))
setIconSize(new qSize(100,100)) }
func pExit()
win.close()
app.quit()It's not currently possible to run a DOME application online in a browser but the following is designed to look and work more or less like the Ring entry. However, the user interface is not as fancy as I don't have ready made images of elevators etc. available.
import "dome" for Window, Platform, Process
import "graphics" for Canvas, Color, Font
import "audio" for AudioEngine
import "input" for Mouse
import "random" for Random
import "./dynamic" for Enum
import "./ellipse" for Circle, Polygon, Rectangle, Square
var Direction = Enum.create("Direction", ["halted", "up", "down"])
var Rand = Random.new()
class Elevator {
construct new() {
Window.resize(820, 820)
Canvas.resize(820, 820)
Window.title = "Elevator game"
// see Go-fonts page
Font.load("Go-Regular20", "Go-Regular.ttf", 20)
Canvas.font = "Go-Regular20"
Font.load("Go-Regular25", "Go-Regular.ttf", 25)
// download from https://soundbible.com/509-Mouse-Double-Click.html
AudioEngine.load("click", "mouse_click.wav")
}
init() {
newGame()
}
newGame() {
_buttons = []
// floors
for (i in 1..7) {
Canvas.rectfill(120, 100*i + 5, 90, 90, Color.white)
}
// floor up/down buttons
Canvas.rectfill(300, 30, 100, 200, Color.orange)
var up = Square.new(315, 45, 70, "up")
up.drawfill(Color.lightgray)
_buttons.add(up)
Canvas.print("\u25b2", 345, 70, Color.black)
var down = Square.new(315, 145, 70, "down")
down.drawfill(Color.lightgray)
_buttons.add(down)
Canvas.print("\u25bc", 345, 170, Color.black)
// floor number buttons
Canvas.rectfill(250, 305, 200, 490, Color.orange)
var f25 = "Go-Regular25"
var floor5 = Circle.new(300, 350, 40, 5)
floor5.drawfill(Color.lightgray)
Canvas.print("5", 295, 340, Color.black, f25)
var floor6 = Circle.new(400, 350, 40, 6)
floor6.drawfill(Color.lightgray)
Canvas.print("6", 395, 340, Color.black, f25)
var floor3 = Circle.new(300, 450, 40, 3)
floor3.drawfill(Color.lightgray)
Canvas.print("3", 295, 440, Color.black, f25)
var floor4 = Circle.new(400, 450, 40, 4)
floor4.drawfill(Color.lightgray)
Canvas.print("4", 395, 440, Color.black, f25)
var floor1 = Circle.new(300, 550, 40, 1)
floor1.drawfill(Color.lightgray)
Canvas.print("1", 295, 540, Color.black, f25)
var floor2 = Circle.new(400, 550, 40, 2)
floor2.drawfill(Color.lightgray)
Canvas.print("2", 395, 540, Color.black, f25)
var ground = Square.fromCenter(350, 650, 80, "ground")
ground.drawfill(Color.white)
Canvas.print("GF", 335, 640, Color.black, f25)
var close = Square.fromCenter(350, 750, 80, "close")
close.drawfill(Color.white, Color.black, 2)
Canvas.print("\u25ba\u25c4", 330, 740, Color.black)
_buttons.addAll([floor1, floor2, floor3, floor4, floor5, floor6, ground, close])
// new game button
var newGame = Rectangle.new(525, 355, 150, 50, "new")
newGame.drawfill(Color.orange)
Canvas.print("New Game", 545, 365, Color.black)
_buttons.add(newGame)
// exit button
var exit = Rectangle.new(525, 595, 150, 50, "exit")
exit.drawfill(Color.orange)
Canvas.print("Exit", 575, 605, Color.black)
_buttons.add(exit)
_floor = Rand.int(0, 7) // start by placing lift on a random floor
drawLift()
openDoors()
changeFloorNumber()
_dir = Direction.halted
_floorDest = _floor
_start = 0
_wait = Rand.int(0, 7) // start with a random wait point, different to where lift is
if (_wait == _floor) {
_wait = _floor - 1
if (_wait < 0) _wait = 2
}
var wait = Polygon.regular(4, 165, 750 - _wait*100, 45, 0)
wait.drawfill(Color.yellow, Color.black)
Canvas.print("WAIT", 140, 740 - _wait*100, Color.black)
}
update() {
if (Mouse["left"].justPressed) {
var x = Mouse.x
var y = Mouse.y
for (btn in _buttons) {
if (btn.contains(x, y)) {
var t = btn.tag
if (t == "up") {
if (_floor < 6) {
_dir = Direction.up
_floorDest = 6
}
} else if (t == "down") {
if (_floor > 0) {
_dir = Direction.down
_floorDest = 0
}
} else if (t is Num) {
if (t < _floor) {
_dir = Direction.down
} else if (t > _floor) {
_dir = Direction.up
} else {
_dir = Direction.halted
openDoors()
}
_floorDest = t
} else if (t == "ground") {
if (_floor > 0) {
_dir = Direction.down
} else {
_dir = Direction.halted
openDoors()
}
_floorDest = 0
} else if (t == "close") {
if (_dir == Direction.halted) {
closeDoors()
}
} else if (t == "new") {
Canvas.cls("black")
newGame()
} else if (t == "exit") {
Process.exit()
}
if (_dir != Direction.halted) {
closeDoors()
_start = Platform.time
} else {
_start = 0
}
}
}
}
}
drawLift() {
Canvas.rectfill(120, 100*(7 - _floor) + 5, 90, 90, Color.blue)
}
undrawLift() {
Canvas.rectfill(120, 100*(7 - _floor) + 5, 90, 90, Color.white)
}
changeFloorNumber() {
Canvas.rectfill(120, 30, 90, 40, Color.orange)
Canvas.print(_floor.toString, 160, 40, Color.black)
}
openDoors() {
Canvas.rectfill(135, 100*(7 - _floor) + 30, 60, 60, Color.white)
}
closeDoors() {
Canvas.rectfill(120, 100*(7 - _floor) + 5, 90, 90, Color.blue)
}
draw(alpha) {
if (_start > 0) {
var delta = Platform.time - _start
if (delta >= 1) {
undrawLift()
_floor = (_dir == Direction.up) ? _floor + 1 : _floor - 1
drawLift()
changeFloorNumber()
if (_floor == _wait) {
_dir = Direction.halted
_start = 0
_wait = -1
openDoors()
} else if (_floor == _floorDest) {
_dir = Direction.halted
_start = 0
openDoors()
} else {
_start = _start + delta
}
}
}
}
}
var Game = Elevator.new()