Skip to content

Commit

Permalink
Add MazeGame SandSimulation WordClock
Browse files Browse the repository at this point in the history
Signed-off-by: Andy Liu <andy@madmachine.io>
  • Loading branch information
andy0808 committed Dec 5, 2024
1 parent b646cde commit dd0205f
Show file tree
Hide file tree
Showing 21 changed files with 1,196 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Examples/SwiftIOPlayground/13MoreProjects/MazeGame/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
17 changes: 17 additions & 0 deletions Examples/SwiftIOPlayground/13MoreProjects/MazeGame/Package.mmp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This is a MadMachine project file in TOML format
# This file holds those parameters that could not be managed by SwiftPM
# Edit this file would change the behavior of the building/downloading procedure
# Those project files in the dependent libraries would be IGNORED

# Specify the board name below
# There are "SwiftIOBoard" and "SwiftIOMicro" now
board = "SwiftIOMicro"

# Specifiy the target triple below
# There are "thumbv7em-unknown-none-eabi" and "thumbv7em-unknown-none-eabihf" now
# If your code use significant floating-point calculation,
# plz set it to "thumbv7em-unknown-none-eabihf"
triple = "thumbv7em-unknown-none-eabi"

# Reserved for future use
version = 1
29 changes: 29 additions & 0 deletions Examples/SwiftIOPlayground/13MoreProjects/MazeGame/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "MazeGame",
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/madmachineio/SwiftIO.git", branch: "develop"),
.package(url: "https://github.com/madmachineio/MadBoards.git", branch: "develop"),
.package(url: "https://github.com/madmachineio/MadDrivers.git", branch: "develop"),
.package(path: "/Users/andy/Documents/MadGraphics")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "MazeGame",
dependencies: [
"SwiftIO",
"MadBoards",
// Use specific library name rather than "MadDrivers" would speed up the build procedure.
.product(name: "ST7789", package: "MadDrivers"),
.product(name: "LIS3DH", package: "MadDrivers"),
"MadGraphics"
]),
]
)
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import MadGraphics

struct Ball {
var x1: Int
var y1: Int
let size: Int

var x2: Int {
x1 + size
}
var y2: Int {
y1 + size
}

init(at point: Point, size: Int) {
x1 = point.x
y1 = point.y
self.size = size
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import MadGraphics
import ST7789
import SwiftIO

// Place a ball at the upper left corner of the maze and move it based on acceleration.
// If the ball reaches the destination (bottom right), the game ends.
// Press the D1 button to restart the game.
class Game {
var maze: Maze
var ball: Ball
let ballColor = Color(UInt32(0xEFE891))

let width = 20
var speed = 2

let screen: ST7789
let layer: Layer
var frameBuffer: [UInt32]
var screenBuffer: [UInt16]

init(screen: ST7789, layer: Layer) {
ball = Ball(at: Point(x: 1, y: 1), size: 7)
maze = Maze(width: width, layer: layer)
frameBuffer = [UInt32](repeating: 0, count: layer.bounds.width * layer.bounds.height)
screenBuffer = [UInt16](repeating: 0, count: layer.bounds.width * layer.bounds.height)
self.screen = screen
self.layer = layer

maze.generate(layer)

layer.draw() { canvas in
canvas.fillRectangle(at: Point(ball.x1, ball.y1), width: ball.size, height: ball.size, data: ballColor.rawValue)
}

layer.render(into: &frameBuffer, output: &screenBuffer, transform: Color.getRGB565LE) { dirty, data in
screen.writeBitmap(x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height, data: data)
}
}

// Create a new maze and place the ball at the starting point.
func reset() {
maze.reset(layer)
maze.generate(layer)

layer.draw() { canvas in
canvas.fillRectangle(at: Point(ball.x1, ball.y1), width: ball.size, height: ball.size, data: maze.bgColor.rawValue)
}
ball = Ball(at: Point(x: 1, y: 1), size: 7)

layer.draw() { canvas in
canvas.fillRectangle(at: Point(ball.x1, ball.y1), width: ball.size, height: ball.size, data: ballColor.rawValue)
}

layer.render(into: &frameBuffer, output: &screenBuffer, transform: Color.getRGB565LE) { dirty, data in
screen.writeBitmap(x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height, data: data)
}
}

// Update the display to show that the game has finished.
func finishGame() {
layer.draw() { canvas in
canvas.fillRectangle(at: Point(0, 0), width: canvas.width, height: canvas.height, data: Color.red.rawValue)
}

let font = Font(path: "/lfs/Resources/Fonts/Roboto-Regular.ttf", pointSize: 10, dpi: 220)

let text1 = TextLayer(at: Point(x: layer.bounds.size.halfWidth, y: 60), anchorPoint: UnitPoint.center, string: "Good Job!", font: font, foregroundColor: Color.white)

font.setSize(pointSize: 6)
let text2 = TextLayer(at: Point(x: layer.bounds.size.halfWidth, y: 140), anchorPoint: UnitPoint.center, string: "Press D1 to continue", font: font, foregroundColor: Color.white)

layer.append(text1)
layer.append(text2)

layer.render(into: &frameBuffer, output: &screenBuffer, transform: Color.getRGB565LE) { dirty, data in
screen.writeBitmap(x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height, data: data)
}

layer.removeAll()
}

// Verify if the ball has reached the bottom right corner of the maze.
func finished() -> Bool {
return ball.x1 / width == maze.column - 1 && ball.y1 / width == maze.row - 1
}

// Update the ball's position based on the acceleration.
func update(_ acceleration: (x: Float, y: Float, z: Float)) {
guard !finished() else {
finishGame()
return
}

let lastBallPos = Point(ball.x1, ball.y1)

// Move to the left.
if acceleration.x > 0.25 {
ball.x1 -= speed

let gridXmin = max(ball.x1 / width, 0)
let gridXmax = min(ball.x2 / width, maze.column - 1)
let gridYmin = max(ball.y1 / width, 0)
let gridYmax = min(ball.y2 / width, maze.row - 1)

// Check if the ball collides with any walls.
// If it does, reposition it close to the wall.
for y in gridYmin...gridYmax {
for x in gridXmin...gridXmax {
let result = checkGridWalls(ballPos: Point(ball.x1, ball.y1), gridPos: Point(x, y))

if result.top || result.bottom || result.right {
ball.x1 = (x + 1) * width + 1
}

if result.left {
ball.x1 = x * width + 1
}
}
}
}

// Move to the right
if acceleration.x < -0.25 {
ball.x1 += speed

let gridXmin = max(ball.x1 / width, 0)
let gridXmax = min(ball.x2 / width, maze.column - 1)
let gridYmin = max(ball.y1 / width, 0)
let gridYmax = min(ball.y2 / width, maze.row - 1)

for y in gridYmin...gridYmax {
for x in gridXmin...gridXmax {
let result = checkGridWalls(ballPos: Point(ball.x1, ball.y1), gridPos: Point(x, y))

if result.top || result.bottom || result.left {
ball.x1 = x * width - ball.size - 1
}

if result.right {
ball.x1 = (x + 1) * width - ball.size - 1
}
}
}
}

// Move downwards.
if acceleration.y > 0.25 {
ball.y1 += speed

let gridXmin = max(ball.x1 / width, 0)
let gridXmax = min(ball.x2 / width, maze.column - 1)
let gridYmin = max(ball.y1 / width, 0)
let gridYmax = min(ball.y2 / width, maze.row - 1)

for y in gridYmin...gridYmax {
for x in gridXmin...gridXmax {
let result = checkGridWalls(ballPos: Point(ball.x1, ball.y1), gridPos: Point(x, y))

if result.bottom {
ball.y1 = (y + 1) * width - 1 - ball.size
}

if result.top || result.right || result.left {
ball.y1 = y * width - 1 - ball.size
}
}
}
}

// Move upwards.
if acceleration.y < -0.25 {
ball.y1 -= speed

let gridXmin = max(ball.x1 / width, 0)
let gridXmax = min(ball.x2 / width, maze.column - 1)
let gridYmin = max(ball.y1 / width, 0)
let gridYmax = min(ball.y2 / width, maze.row - 1)

for y in gridYmin...gridYmax {
for x in gridXmin...gridXmax {
let result = checkGridWalls(ballPos: Point(ball.x1, ball.y1), gridPos: Point(x, y))

if result.top {
ball.y1 = y * width + 1
}

if result.bottom || result.right || result.left {
ball.y1 = (y + 1) * width + 1
}
}
}
}

// If the ball's position has changed, update the display.
if lastBallPos.x != ball.x1 || lastBallPos.y != ball.y1 {
layer.draw() { canvas in
canvas.fillRectangle(at: lastBallPos, width: ball.size, height: ball.size, data: maze.bgColor.rawValue)
}
layer.draw() { canvas in
canvas.fillRectangle(at: Point(ball.x1, ball.y1), width: ball.size, height: ball.size, data: ballColor.rawValue)
}
layer.render(into: &frameBuffer, output: &screenBuffer, transform: Color.getRGB565LE) { dirty, data in
screen.writeBitmap(x: dirty.x, y: dirty.y, width: dirty.width, height: dirty.height, data: data)
}
}
}

// Check if the ball collides with a wall.
func checkCollision(ballPos: Point, wallP1: Point, wallP2: Point) -> Bool {
return ball.x1 <= wallP2.x && ball.x2 >= wallP1.x && ball.y1 <= wallP2.y && ball.y2 >= wallP1.y
}

// Check if the ball collides with any wall of a cell in the maze grid.
func checkGridWalls(ballPos: Point, gridPos: Point) -> Wall {
let walls = maze.grids[maze.getIndex(gridPos)].walls
var result = Wall(top: false, right: false, bottom: false, left: false)

if walls.top &&
checkCollision(ballPos: ballPos, wallP1: Point(gridPos.x * width, gridPos.y * width), wallP2: Point((gridPos.x + 1) * width, gridPos.y * width)) {
result.top = true
}

if walls.right &&
checkCollision(ballPos: ballPos, wallP1: Point((gridPos.x + 1) * width, gridPos.y * width), wallP2: Point((gridPos.x + 1) * width, (gridPos.y + 1) * width)) {
result.right = true

}

if walls.bottom &&
checkCollision(ballPos: ballPos, wallP1: Point(gridPos.x * width, (gridPos.y + 1) * width), wallP2: Point((gridPos.x + 1) * width, (gridPos.y + 1) * width)) {
result.bottom = true
}

if walls.left &&
checkCollision(ballPos: ballPos, wallP1: Point(gridPos.x * width, gridPos.y * width), wallP2: Point(gridPos.x * width, (gridPos.y + 1) * width)) {
result.left = true
}

return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
struct Wall {
var top: Bool
var right: Bool
var bottom: Bool
var left: Bool
}

struct Grid {
let x: Int
let y: Int
var walls = Wall(top: true, right: true, bottom: true, left: true)
var visited = false
}
Loading

0 comments on commit dd0205f

Please sign in to comment.