In the beginning there was darkness

This commit is contained in:
Daniel Ziltener 2021-01-03 18:54:03 +01:00
commit cb97b35d4e
7 changed files with 1614 additions and 0 deletions

368
board.scm Normal file
View File

@ -0,0 +1,368 @@
;; -*- geiser-scheme: chicken -*-
(module board
*
(import scheme
(chicken base)
(chicken random)
(chicken plist)
miscmacros
srfi-1
srfi-123
mini-kanren
uuid
(prefix sdl2 "sdl2:")
(prefix sdl2-image "img:")
(prefix sdl2-ttf "ttf:"))
(ttf:init!)
(define logo-font (ttf:open-font "resources/ui/SourceSerifPro-Regular.otf" 80))
(define background #f)
(define house #f)
(define flower #f)
(define dicesym #f)
(define flower-rotation 0)
(define player-offset '((1 400 . 250)
(2 400 . 835)))
(define tiles #f)
(define white-bottom #f)
(define white-top #f)
(define black-bottom #f)
(define black-top #f)
(define state #f)
(define orig-board
'(;; Torso
((0 . 0) . flower:)
((0 . 1) . gate:)
((0 . 2) . flower:)
((1 . 0) . temple:)
((1 . 1) . water:)
((1 . 2) . temple:)
((2 . 0) . water:)
((2 . 1) . market:)
((2 . 2) . water:)
((3 . 0) . temple:)
((3 . 1) . flower:)
((3 . 2) . temple:)
;; Neck
((4 . 1) . water:)
((5 . 1) . market:)
;; Head
((6 . 0) . flower:)
((6 . 1) . temple:)
((6 . 2) . flower:)
((7 . 0) . treasury:)
((7 . 1) . water:)
((7 . 2) . treasury:)))
(define player-paths
'((1 . ((1 . (3 . 0))
(2 . (2 . 0))
(3 . (1 . 0))
(4 . (0 . 0))
(5 . (0 . 1))
(6 . (1 . 1))
(7 . (2 . 1))
(8 . (3 . 1))
(9 . (4 . 1))
(10 . (5 . 1))
(11 . (6 . 1))
(12 . (7 . 1))
(13 . (7 . 0))
(14 . (6 . 0))
(15 . (5 . 0))))
(2 . ((1 . (3 . 2))
(2 . (2 . 2))
(3 . (1 . 2))
(4 . (0 . 2))
(5 . (0 . 1))
(6 . (1 . 1))
(7 . (2 . 1))
(8 . (3 . 1))
(9 . (4 . 1))
(10 . (5 . 1))
(11 . (6 . 1))
(12 . (7 . 1))
(13 . (7 . 2))
(14 . (6 . 2))
(15 . (5 . 2))))))
(define (grid-rect x y)
(sdl2:make-rect (+ 500 (* x 120))
(+ 400 (* y 120))
120 120))
(define (init-piece player id)
(let ((coords (sdl2:make-rect (+ (car (alist-ref player player-offset)) (* id 100))
(cdr (alist-ref player player-offset)) 75 75)))
`((uuid: . ,(uuid))
(player: . ,player)
(state: . top:)
(initial-coords: . ,coords)
(coords: . ,coords)
(target-coords: . ,coords)
(step: . 0))))
(define (initialize-state!)
(set! state
`((active-player: . 1)
(players: . ((1 . ((type: . local:)
(state: . begin:)
(piece-y-offset: . -10)))
(2 . ((type: . local:)
(state: . wait:)
(piece-y-offset: . 10)))))
(pieces: . (,(init-piece 1 0)
,(init-piece 1 1)
,(init-piece 1 2)
,(init-piece 1 3)
,(init-piece 1 4)
,(init-piece 1 5)
,(init-piece 1 6)
,(init-piece 2 0)
,(init-piece 2 1)
,(init-piece 2 2)
,(init-piece 2 3)
,(init-piece 2 4)
,(init-piece 2 5)
,(init-piece 2 6))))))
(define (refo coll key val)
(membero (cons key val) coll))
(define (field coords type)
(membero (cons coords type) orig-board))
;; When given a piece, returns its board x y position.
;; When given a board position, returns the piece.
(define (piece-pos piece pos)
(fresh (player piece-step player-path) ; Initialize empty variables
(membero piece (cdr (assoc pieces: state))) ; The piece is a member of the state alist
(refo piece player: player) ; The player number is under key "player:" in the piece alist.
(refo piece step: piece-step) ; The piece step is under key "step:" in the piece alist.
(refo player-paths player player-path) ; The player-path is under the player number in the "player-paths" alist
(=/= piece-step 0) ; The piece step cannot be 0
(refo player-path piece-step pos))) ; The position is under piece-step in the "player-path" alist.
;; Given a piece and a diceroll, returns the board x y position
;; the piece will have to move to.
(define (piece-targetpos piece dice out)
(fresh (player piece-step piece-targetstep player-path) ; Initialize empty variables
(membero piece (cdr (assoc pieces: state))) ; The piece is a member of the state alist
(refo piece player: player) ; The player number is the value of the piece entry with key "player:"
(refo piece step: piece-step) ; The step number is the value of the piece entry with key "step:"
(numbero piece-step) ; piece-step is a number
(refo player-paths player player-path) ; The played piece path is the value of the player-paths entry with key player
(refo player-path piece-targetstep out) ; Set "out" to the player-path entry found under key piece-targetstep
(project (piece-step) ; Turn piece-step from a logic variable into a usable value...
(== (+ piece-step dice) piece-targetstep)))) ; and set piece-targetstep to the sum of piece-step plus the dice value
;; Given a piece and a dice value, returns the actions to be performed on the board.
(define (check-move piece dice out)
(fresh (player piece-uuid target-uuid newpiece-pos target-player target-piece)
(refo piece player: player)
(refo piece uuid: piece-uuid)
(listo out)
(conda ((piece-targetpos piece dice newpiece-pos) ; Given the target would be on the board
(conda ; v Subconditions
((piece-pos target-piece newpiece-pos) ; | Given there is a piece on the target house
(conda ((refo target-piece player: player) ; | It's the player's faction
(membero (cons block: #t) out)) ; | | => Deny the movement
((refo target-piece uuid: target-uuid) ; | It's the enemy faction
(conda ((field newpiece-pos flower:) ; | | Given the piece would end up on flower
(membero (cons block: #t) out)) ; | | | => Deny the movement
; | | Else: yeet the piece back to start
((membero (cons reset: target-uuid) out) ; | | | => Reset the enemy piece
(membero (cons move: piece-uuid) out)))))); | | | => Move the player piece
; | Else: The target house is clear
((conda ((field newpiece-pos flower:) ; | | Given the piece would end up on flower
(membero (cons move: piece-uuid) out) ; | | | => Move the piece
(membero (cons state: (cons player begin:)) out)) ; | | | => Set the state so the dice can be rolled again
((membero (cons move: piece-uuid) out)))) ; | | Else: move the piece
((membero (cons move: piece-uuid) out)))) ; | => Move the player piece
; Else: Target would be off board
((membero (cons block: #t) out))))) ; | => Deny the movement
(define (check-can-player-move player dice out)
(fresh (piece commands) ; Initialize empty variables
(membero piece (cdr (assoc pieces: state))) ; The piece is a member of the state list
(refo piece player: player) ; It belongs to the active player
(check-move piece dice commands) ; Determine of the piece can move given the dice roll
(conda ((membero (cons block: #t) commands) ; Given the call to "check-move" returns a blocking command
(== out #f)) ; | Return false
((== out #t))))) ; Else: return true
(define (load-texture renderer file)
(sdl2:create-texture-from-surface renderer (img:load file)))
(define (prepare! renderer)
(set! flower (load-texture renderer "resources/tiles/flower.png"))
(set! dicesym (load-texture renderer "resources/sprites/die0.png"))
(set! tiles
`((flower: . ,(load-texture renderer "resources/tiles/flower.png"))
(gate: . ,(load-texture renderer "resources/tiles/gate.png"))
(water: . ,(load-texture renderer "resources/tiles/water.png"))
(temple: . ,(load-texture renderer "resources/tiles/temple.png"))
(treasury: . ,(load-texture renderer "resources/tiles/treasury.png"))
(market: . ,(load-texture renderer "resources/tiles/market.png"))))
(set! background (load-texture renderer "resources/ui/gate.jpeg"))
(set! house (load-texture renderer "resources/tiles/house.png"))
(set! white-bottom (load-texture renderer "resources/sprites/piece-white-bottom.png"))
(set! white-top (load-texture renderer "resources/sprites/piece-white-top.png"))
(set! black-bottom (load-texture renderer "resources/sprites/piece-black-bottom.png"))
(set! black-top (load-texture renderer "resources/sprites/piece-black-top.png"))
(initialize-state!)
#f)
(define (find-piece-for-uuid uuid)
(find (lambda (piece)
(eq? uuid (alist-ref uuid: piece)))
(alist-ref pieces: state)))
(define (reset-piece! piece-uuid)
(let ((piece (find-piece-for-uuid piece-uuid)))
(print "Reset piece " piece)
(set-cdr! (assoc step: piece) 0)
(set-cdr! (assoc coords: piece) (alist-ref initial-coords: piece))))
(define (move-piece! piece-uuid steps)
(let ((piece (find-piece-for-uuid piece-uuid)))
(print "Move piece " piece)
(let* ((active-player (alist-ref active-player: state))
(players (alist-ref players: state))
(player (alist-ref active-player players))
(piece-y-offset (alist-ref piece-y-offset: player))
(new-step (if (< (alist-ref step: piece) 15)
(+ steps (alist-ref step: piece))
(alist-ref step: piece)))
(player-path (alist-ref active-player player-paths))
(coords (alist-ref new-step player-path))
(rect (grid-rect (car coords) (cdr coords))))
(set! (sdl2:rect-x rect) (+ (sdl2:rect-x rect) 20))
(set! (sdl2:rect-y rect) (+ (sdl2:rect-y rect) 20 piece-y-offset))
(set! (sdl2:rect-w rect) 75)
(set! (sdl2:rect-h rect) 75)
(set-cdr! (assoc step: piece) new-step)
(set-cdr! (assoc coords: piece) rect)
(set-cdr! (assoc active-player: state) (if (eq? 1 active-player) 2 1)))))
(define (check-piececlick)
(let/cc break
(let* ((active-player (alist-ref active-player: state))
(piece (find (lambda (x)
(and (sdl2:point-in-rect? (get 'sdlprops 'mouse-point) (alist-ref coords: x))
(eq? (alist-ref player: x) active-player)))
(alist-ref pieces: state)))
(piece (if (eqv? #f piece) (break #t) piece)) ;; Jump out if no piece clicked.
(pos (alist-ref step: piece))
(dice 1)
(instructions (car (run 1 (q) (check-move piece dice q)))))
(for-each
(lambda (instruction)
(let ((inst (car instruction))
(arg (cdr instruction)))
(case inst
((block:) #t)
((reset:) (reset-piece! arg))
((move:) (move-piece! arg dice)))))
instructions)
;; (for-each
;; (lambda (piece)
;; (when (and (sdl2:point-in-rect? (get 'sdlprops 'mouse-point) (alist-ref coords: piece))
;; (eq? (alist-ref player: piece)
;; active-player))
;; (move-piece! piece 1)))
;; (alist-ref pieces: state))
)))
(define (handle-event ev)
(case (sdl2:event-type ev)
((mouse-button-up)
(check-piececlick))))
(define (update-scene!)
(set! flower-rotation (modulo (ceiling (/ (get 'sdlprops 'ticks) 50))
360))
#f)
(define (render-logo! window)
(let*-values (((text-w text-h) (ttf:size-utf8 logo-font "The Royal Game of Ur")))
(sdl2:render-copy-ex! window flower
#f (sdl2:make-rect 50 50 100 100) flower-rotation)
(sdl2:render-copy-ex! window flower
#f (sdl2:make-rect 1770 50 100 100) flower-rotation)
(sdl2:render-copy! window
(sdl2:create-texture-from-surface window
(ttf:render-utf8-blended logo-font "The Royal Game of Ur" (sdl2:make-color 0 0 0)))
#f (sdl2:make-rect (- (/ 1920 2) (/ text-w 2)) 10 text-w text-h))
))
(define (render-field! renderer type x y)
(let* ((rect (grid-rect x y))
(tilerect (sdl2:make-rect (+ 10 (sdl2:rect-x rect))
(+ 10 (sdl2:rect-y rect))
100 100)))
(sdl2:render-copy! renderer house #f rect)
(sdl2:render-copy! renderer (alist-ref type tiles) #f tilerect)))
(define (render-dice! renderer)
(sdl2:render-copy! renderer dicesym #f (sdl2:make-rect 1600 700 75 75)))
(define (draw-orig-board! renderer)
(sdl2:render-draw-colour-set! renderer (sdl2:make-colour 0 0 0 0))
;; Board
(for-each (lambda (field)
(render-field! renderer (cdr field) (caar field) (cdar field)))
orig-board)
;; Outline
;; Left border torso
(sdl2:render-fill-rect! renderer (sdl2:make-rect 495 400 10 (* 120 3)))
;; Top border torso
(sdl2:render-fill-rect! renderer (sdl2:make-rect 495 395 (+ 10 (* 120 4)) 10))
;; Bottom border torso
(sdl2:render-fill-rect! renderer (sdl2:make-rect 495 755 (+ 10 (* 120 4)) 10))
;; Right upper border torso
(sdl2:render-fill-rect! renderer (sdl2:make-rect 975 395 10 (+ 10 120)))
;; Right lower border torso
(sdl2:render-fill-rect! renderer (sdl2:make-rect 975 635 10 (+ 10 120)))
;; Top border neck
(sdl2:render-fill-rect! renderer (sdl2:make-rect 975 515 (+ 10 (* 120 2)) 10))
;; Bottom border neck
(sdl2:render-fill-rect! renderer (sdl2:make-rect 975 635 (+ 10 (* 120 2)) 10))
;; Left upper border head
(sdl2:render-fill-rect! renderer (sdl2:make-rect 1215 395 10 (+ 10 120)))
;; Left lower border head
(sdl2:render-fill-rect! renderer (sdl2:make-rect 1215 635 10 (+ 10 120)))
;; Top border head
(sdl2:render-fill-rect! renderer (sdl2:make-rect 1215 395 (+ 10 (* 120 2)) 10))
;; Bottom border head
(sdl2:render-fill-rect! renderer (sdl2:make-rect 1215 755 (+ 10 (* 120 2)) 10))
;; Right border head
(sdl2:render-fill-rect! renderer (sdl2:make-rect 1455 395 10 (+ 10 (* 120 3)))))
(define (draw-pieces! renderer)
(for-each (lambda (piece)
(sdl2:render-copy! renderer
(if (eq? 1 (alist-ref player: piece))
(if (eq? top: (alist-ref state: piece))
black-top black-bottom)
(if (eq? top: (alist-ref state: piece))
white-top white-bottom))
#f (alist-ref coords: piece)))
(alist-ref pieces: state)))
(define (draw-scene! renderer)
(sdl2:render-copy! renderer background)
(render-logo! renderer)
(draw-orig-board! renderer)
(draw-pieces! renderer)
(render-dice! renderer)
)
)

91
main.scm Normal file
View File

@ -0,0 +1,91 @@
;; -*- geiser-scheme: chicken -*-
(include "menu.scm")
(include "board.scm")
(import scheme
(chicken base)
(chicken plist)
(chicken condition)
(chicken format)
(prefix sdl2 "sdl2:")
(prefix sdl2-image "img:")
(prefix sdl2-ttf "ttf:")
(prefix menu "menu:")
(prefix board "board:")
miscmacros)
(sdl2:set-main-ready!)
(sdl2:init! '(video timer events))
(ttf:init!)
(sdl2:set-hint! 'render-scale-quality "best")
(on-exit sdl2:quit!)
(current-exception-handler
(let ((original-handler (current-exception-handler)))
(lambda (exception)
(sdl2:quit!)
(original-handler exception))))
(define window (sdl2:create-window! "The Royal Game of Alnothur" 'centered 'centered 1920 1080 '(opengl)))
(define renderer (sdl2:create-renderer! window -1 '(accelerated target-texture)))
(sdl2:render-draw-colour-set! renderer (sdl2:make-colour 128 128 128 0))
(sdl2:render-fill-rect! renderer (sdl2:make-rect 0 0 1920 1080))
(sdl2:delay! 10)
(put! 'sdlprops 'mouse-point (sdl2:make-point 0 0))
(put! 'sdlprops 'old-ticks (sdl2:get-ticks))
(put! 'progprops 'current-state #f)
(put! 'progprops 'next-state menu:)
(define (draw-scene!)
(when (not (equal? (get 'progprops 'current-state)
(get 'progprops 'next-state)))
(put! 'progprops 'current-state (get 'progprops 'next-state))
(sdl2:render-draw-colour-set! renderer (sdl2:make-colour 128 128 128 0))
(sdl2:render-fill-rect! renderer (sdl2:make-rect 0 0 1920 1080))
(case (get 'progprops 'next-state)
((menu:) (menu:prepare! renderer))
((board:) (board:prepare! renderer))))
(case (get 'progprops 'current-state)
((menu:) (menu:draw-scene! renderer))
((board:) (board:draw-scene! renderer)))
(sdl2:render-present! renderer))
(define (update-scene!)
(case (get 'progprops 'current-state)
((menu:) (menu:update-scene!))
((board:) (board:update-scene!))))
(define (handle-event ev exit-main-loop!)
(case (sdl2:event-type ev)
((window) (draw-scene!))
((quit) (exit-main-loop! #t))
((mouse-motion)
(put! 'sdlprops 'mouse-x (sdl2:mouse-motion-event-x ev))
(put! 'sdlprops 'mouse-y (sdl2:mouse-motion-event-y ev))
(put! 'sdlprops 'mouse-point (sdl2:make-point (get 'sdlprops 'mouse-x) (get 'sdlprops 'mouse-y))))
((key-down)
(case (sdl2:keyboard-event-sym ev))
(draw-scene!)))
(case (get 'progprops 'current-state)
((menu:) (menu:handle-event ev))
((board:) (board:handle-event ev))))
(let/cc exit-main-loop!
(while #t
(sdl2:pump-events!)
(while (sdl2:has-events?)
(handle-event (sdl2:poll-event!) exit-main-loop!))
(put! 'sdlprops 'ticks (sdl2:get-ticks))
(put! 'sdlprops 'tickdelta (- (get 'sdlprops 'ticks)
(get 'sdlprops 'old-ticks)))
(draw-scene!)
(update-scene!)
(put! 'sdlprops 'old-ticks (get 'sdlprops 'ticks))
(sdl2:delay! 10)))
(sdl2:destroy-renderer! renderer)
(sdl2:quit!)

105
menu.scm Normal file
View File

@ -0,0 +1,105 @@
;; -*- geiser-scheme: chicken -*-
(module menu
*
(import scheme
(chicken base)
(chicken plist)
srfi-1
(prefix sdl2 "sdl2:")
(prefix sdl2-image "img:")
(prefix sdl2-ttf "ttf:"))
(ttf:init!)
(define logo-font (ttf:open-font "resources/ui/SourceSerifPro-Regular.otf" 80))
(define button-font (ttf:open-font "resources/ui/SourceSerifPro-Regular.otf" 50))
(define (render-button renderer text)
(let*-values (((button) (img:load "resources/ui/button.png"))
((button-hover) (img:load "resources/ui/button-hover.png"))
((button-w) (sdl2:surface-w button))
((button-h) (sdl2:surface-h button))
((text-w text-h) (ttf:size-utf8 button-font text))
((text-x) (round (- (/ button-w 2) (/ text-w 2))))
((text-y) (round (- (/ button-h 2) (/ text-h 2)))))
(sdl2:blit-surface!
(ttf:render-utf8-blended button-font text (sdl2:make-color 0 0 0)) #f
button
(sdl2:make-rect text-x text-y text-w text-h))
(sdl2:blit-surface!
(ttf:render-utf8-blended button-font text (sdl2:make-color 0 0 0)) #f
button-hover
(sdl2:make-rect text-x text-y text-w text-h))
(list (cons surface: (sdl2:create-texture-from-surface renderer button))
(cons surface-hover: (sdl2:create-texture-from-surface renderer button-hover))
(cons width: button-w)
(cons height: button-h))))
(define (make-button renderer text offset-x offset-y on-click)
(let* ((blist (render-button renderer text))
(blist (alist-cons rect: (sdl2:make-rect offset-x offset-y
(cdr (assoc width: blist))
(cdr (assoc height: blist))) blist))
(blist (alist-cons offset-x: offset-x blist))
(blist (alist-cons offset-y: offset-y blist))
(blist (alist-cons on-click: on-click blist)))
blist))
(define background #f)
(define flower #f)
(define flower-rotation 0)
(define buttons #f)
(define (prepare! renderer)
(set! background (sdl2:create-texture-from-surface renderer (img:load "resources/ui/zikkurat.png")))
(set! flower (sdl2:create-texture-from-surface renderer (img:load "resources/tiles/flower.png")))
(set! buttons (list (make-button renderer "Test" 650 300 (lambda () (put! 'progprops 'next-state board:)))
(make-button renderer "Test 2" 650 400 (lambda () (print "Button Test 2 clicked"))))))
(define (check-buttonclick)
(for-each
(lambda (button)
(if (sdl2:point-in-rect? (get 'sdlprops 'mouse-point) (cdr (assoc rect: button)))
((cdr (assoc on-click: button)))))
buttons))
(define (handle-event ev)
(case (sdl2:event-type ev)
((mouse-button-up)
(check-buttonclick))))
(define (render-logo! window)
(let*-values (((text-w text-h) (ttf:size-utf8 logo-font "The Royal Game of Ur")))
(sdl2:render-copy-ex! window flower
#f (sdl2:make-rect 50 50 100 100) flower-rotation)
(sdl2:render-copy-ex! window flower
#f (sdl2:make-rect 1770 50 100 100) flower-rotation)
(sdl2:render-copy! window
(sdl2:create-texture-from-surface window
(ttf:render-utf8-blended logo-font "The Royal Game of Ur" (sdl2:make-color 0 0 0)))
#f (sdl2:make-rect (- (/ 1920 2) (/ text-w 2)) 10 text-w text-h))
))
(define (update-scene!)
(set! flower-rotation (modulo (ceiling (/ (get 'sdlprops 'ticks) 50))
360)))
(define (draw-scene! window)
;; Background
(sdl2:render-copy! window background)
;; Logo
(render-logo! window)
;; Buttons
(for-each
(lambda (button)
(sdl2:render-copy! window
(if (sdl2:point-in-rect? (get 'sdlprops 'mouse-point) (cdr (assoc rect: button)))
(cdr (assoc surface-hover: button)) (cdr (assoc surface: button))) #f
(sdl2:make-rect (cdr (assoc offset-x: button))
(cdr (assoc offset-y: button))
(cdr (assoc width: button))
(cdr (assoc height: button)))))
buttons)
))

1050
resources/drawingboard.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

BIN
resources/ui/gate.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

BIN
resources/ui/zikkurat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB