First simulator solve. Still a lot of bugs
This commit is contained in:
parent
65b11083b2
commit
80b7e884cd
13
README
13
README
|
@ -5,8 +5,17 @@ This creates two main files:
|
|||
* `zig-out/bin/micromouse` which can be run on the host computer in simulation
|
||||
|
||||
# Overview / Assumptions
|
||||
We're using the same coordinate space as the simulator (mms). This is "math coords", with 0, 0 in the lower left and positive y up.
|
||||
We're using the same coordinate space as the simulator (mms). This is "math coords", with 0, 0 in the lower left and positive y up. North is up
|
||||
|
||||
# Code notes
|
||||
I'm using usize as my default int type so that I can index into arrays with it, I don't know if there's a better option.
|
||||
TODO: test with non-square. I did my best but I don't know
|
||||
TODO: test with non-square mazes. I did my best but I don't know
|
||||
|
||||
Use std.log.debug. microzig. yeah
|
||||
|
||||
# Simulation
|
||||
Download the mms simulator from https://github.com/mackorone/mms/releases.
|
||||
(I installed this to my Applications folder.)
|
||||
Pop open a new Mouse in the simulator by clicking the plus next to the "Mouse" selector.
|
||||
Point it at this directory. Set it up with `/usr/local/bin/zig build` as the build command and `./zig-out/bin/micromouse` as the run command.
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
// TODO: extract this into robot or main or something
|
||||
fn print (comptime fmt: []const u8, args: anytype) void {
|
||||
if (@import("builtin").os.tag != .freestanding) {
|
||||
std.debug.print(fmt, args);
|
||||
}
|
||||
}
|
||||
|
||||
const map = @import("map.zig");
|
||||
const Point = map.Point;
|
||||
const Cardinal = map.Cardinal;
|
||||
|
@ -19,11 +26,14 @@ const Node = struct {
|
|||
|
||||
// I could clean this up with like a loop and a helper function
|
||||
// North
|
||||
// std.log.debug("North: y + 1 < height {}; not wall: {}", .{self.p.y + 1 < map.height, !isWall(self.p, .north)});
|
||||
// std.log.debug("isWall(self.p '{}', .north): {}", .{self.p, isWall(self.p, .north)});
|
||||
if (self.p.y + 1 < map.height and !isWall(self.p, .north)) {
|
||||
// std.log.debug("Adding node", .{});
|
||||
nodes.append(&mazeNodes[self.p.y + 1][self.p.x]) catch unreachable;
|
||||
}
|
||||
// South
|
||||
if (self.p.y - 1 >= 0 and !isWall(self.p, .south)) {
|
||||
if (self.p.y > 0 and !isWall(self.p, .south)) {
|
||||
nodes.append(&mazeNodes[self.p.y - 1][self.p.x]) catch unreachable;
|
||||
}
|
||||
// East
|
||||
|
@ -31,7 +41,7 @@ const Node = struct {
|
|||
nodes.append(&mazeNodes[self.p.y][self.p.x + 1]) catch unreachable;
|
||||
}
|
||||
// West
|
||||
if (self.p.x - 1 >= 0 and !isWall(self.p, .west)) {
|
||||
if (self.p.x > 0 and !isWall(self.p, .west)) {
|
||||
nodes.append(&mazeNodes[self.p.y][self.p.x - 1]) catch unreachable;
|
||||
}
|
||||
|
||||
|
@ -103,11 +113,12 @@ var walls = init: {
|
|||
// };
|
||||
|
||||
fn isEdge(p: Point, direction: Cardinal) bool {
|
||||
// std.log.debug("is Edge called with point: {} direction: {}\n", .{p, direction});
|
||||
return (
|
||||
(p.x == 0 and direction == .west)
|
||||
or (p.y == 0 and direction == .north)
|
||||
or (p.y == 0 and direction == .south)
|
||||
or (p.x == map.width - 1 and direction == .east)
|
||||
or (p.y == map.height - 1 and direction == .south)
|
||||
or (p.y == map.height - 1 and direction == .north)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -129,6 +140,7 @@ fn getWall(p: Point, direction: Cardinal) *?bool {
|
|||
fn isWall(p: Point, direction: Cardinal) bool {
|
||||
// Boundaries are definitely there
|
||||
if (isEdge(p, direction)) {
|
||||
// std.log.debug("forcing true return from isWall for point {} and direction {}", .{p, direction});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -141,7 +153,18 @@ fn isWall(p: Point, direction: Cardinal) bool {
|
|||
pub fn setWall(p: Point, direction: Cardinal, value: bool) void {
|
||||
// We should maybe have a better way of handling this if it actually happens to the robot
|
||||
// If that direction is an outer wall, assert that we saw a wall
|
||||
assert(!(isEdge(p, direction) and !value));
|
||||
// TODO: cleanup
|
||||
if (isEdge(p, direction)) {
|
||||
// std.log.debug("Hello? {} {} {} {}", .{p, direction, isEdge(p, direction), value});
|
||||
assert(value);
|
||||
// print("Hello? {} {} {} {}", .{p, direction, isEdge(p, direction), value});
|
||||
// std.debug.panic("Outside of maze is ");
|
||||
}
|
||||
// assert(!(isEdge(p, direction) and !value));
|
||||
|
||||
if (isEdge(p, direction)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wall = getWall(p, direction);
|
||||
// wall.* is a ?bool and value is a bool, will this error?
|
||||
|
@ -206,16 +229,27 @@ fn hasPoint(l: []const Point, needle: Point) bool {
|
|||
// Takes a list of destination points, and a mouse
|
||||
// Blocks until the mouse is in one of the destination points
|
||||
pub fn floodFill(mouse: *Mouse, dest: []const Point) void {
|
||||
mouse.updateWalls();
|
||||
|
||||
// Calculate optimistic distances from the center
|
||||
recalcScores(dest);
|
||||
|
||||
for (dest) |p| {
|
||||
const goalNode = &mazeNodes[p.y][p.x];
|
||||
std.log.debug("goal nodes: {}\n", .{ goalNode });
|
||||
}
|
||||
|
||||
// While we aren't at dest,
|
||||
while (!hasPoint(dest[0..], mouse.position)) {
|
||||
const currentSquare = &mazeNodes[mouse.position.y][mouse.position.x];
|
||||
|
||||
// 1. get the square adjacent to the mouse which has the lowest score
|
||||
var minAdjacent: ?*Node = null;
|
||||
for (currentSquare.getAdjacentNodes().slice()) |n| {
|
||||
std.log.debug("currentSquare {} score: {}", .{ currentSquare.p, currentSquare.score });
|
||||
const adjacentNodes = currentSquare.getAdjacentNodes().slice();
|
||||
std.log.debug("adjacentNodes {}", .{adjacentNodes.len});
|
||||
for (adjacentNodes) |n| {
|
||||
std.log.debug("Node at ({}, {}) has score {}", .{ n.p.x, n.p.y, n.score });
|
||||
assert(@TypeOf(n) == *Node);
|
||||
if (minAdjacent == null or n.score < minAdjacent.?.score) {
|
||||
minAdjacent = n;
|
||||
|
|
|
@ -7,7 +7,7 @@ const map = @import("map.zig");
|
|||
// TODO: Refactor, move the Mouse declaration into algorithm
|
||||
const mouse = @import("mouse.zig");
|
||||
|
||||
pub fn main() !noreturn {
|
||||
pub fn main() !void {
|
||||
var m = mouse.initialize();
|
||||
|
||||
algo.floodFill(&m, map.goals[0..]);
|
||||
|
@ -15,4 +15,8 @@ pub fn main() !noreturn {
|
|||
algo.floodFill(&m, starts[0..]);
|
||||
|
||||
m.stall();
|
||||
// if (!mouse.is_robot) {
|
||||
// @import("simulator.zig").moveForward();
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
14
src/map.zig
14
src/map.zig
|
@ -25,8 +25,8 @@ pub const TurningDirection = enum {
|
|||
};
|
||||
|
||||
// While we're here, we might as well generalize to a non-square maze
|
||||
pub const width = 10;
|
||||
pub const height = 10;
|
||||
pub const width = 16;
|
||||
pub const height = 16;
|
||||
|
||||
pub const numTiles = width * width;
|
||||
|
||||
|
@ -45,12 +45,14 @@ pub const numTiles = width * width;
|
|||
|
||||
pub const goals = [4]Point{
|
||||
Point { .x = width / 2 , .y = height / 2 },
|
||||
Point { .x = width / 2 , .y = height / 2 + 1},
|
||||
Point { .x = width / 2 + 1 , .y = height / 2 },
|
||||
Point { .x = width / 2 + 1 , .y = height / 2 + 1}
|
||||
Point { .x = width / 2 , .y = height / 2 - 1},
|
||||
Point { .x = width / 2 - 1 , .y = height / 2 },
|
||||
Point { .x = width / 2 - 1 , .y = height / 2 - 1}
|
||||
};
|
||||
// This should be compile-time accessible
|
||||
// @compileLog(goals);
|
||||
comptime {
|
||||
// @compileLog(goals);
|
||||
}
|
||||
|
||||
pub fn isGoal (p: Point) bool {
|
||||
for (goals) |goalPoint| {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const assert = @import("std").debug.assert;
|
||||
const abs = @import("std").math.absCast; // builtin in 0.12 please
|
||||
|
||||
const intDist = @import("util.zig").intDist;
|
||||
|
||||
const map = @import("map.zig");
|
||||
const Cardinal = map.Cardinal;
|
||||
const Point = map.Point;
|
||||
|
@ -62,18 +64,21 @@ pub const Mouse = struct {
|
|||
}
|
||||
|
||||
pub fn moveAdjacent (self: *Mouse, p: Point) void {
|
||||
// @abs is coming in Zig 0.12. PLEASE
|
||||
assert(abs(self.position.x - p.x) + abs(self.position.y - p.y) == 1);
|
||||
// |self.position.x - p.x| + |self.position.y - p.y| == 1
|
||||
assert(intDist(self.position.x, p.x) + intDist(self.position.y, p.y) == 1);
|
||||
// assert(@max(self.position.x, p.x) - @min() ==);
|
||||
// assert(abs(@as(isize, self.position.x) - @as(isize, p.x)) + abs(@as(isize, self.position.y) - @as(isize, p.y)) == 1);
|
||||
|
||||
// The direction we need to move to get to this point
|
||||
var direction: Cardinal = undefined;
|
||||
// - 1 has a chance to underflow if it's 0.
|
||||
if (p.x + 1 == self.position.x) {
|
||||
direction = .west;
|
||||
}else if (p.x - 1 == self.position.x) {
|
||||
direction = .east;
|
||||
}else if (p.y + 1 == self.position.y) {
|
||||
direction = .south;
|
||||
}else if (p.y - 1 == self.position.y) {
|
||||
}else if (p.x > 0 and p.x - 1 == self.position.x) {
|
||||
direction = .east;
|
||||
}else if (p.y > 0 and p.y - 1 == self.position.y) {
|
||||
direction = .north;
|
||||
}
|
||||
|
||||
|
@ -81,6 +86,9 @@ pub const Mouse = struct {
|
|||
|
||||
// I haven't decided if this is going to be implicit 1 square or what
|
||||
robot.moveForward();
|
||||
|
||||
// Update our position
|
||||
self.position = p;
|
||||
}
|
||||
|
||||
// These functions return null if that direction is the back (where we don't have a sensor)
|
||||
|
@ -88,32 +96,32 @@ pub const Mouse = struct {
|
|||
fn readNorth(self: Mouse) ?bool {
|
||||
switch (self.facing) {
|
||||
.north => return robot.readFront(),
|
||||
.east => return robot.readRight(),
|
||||
.east => return robot.readLeft(),
|
||||
.south => return null,
|
||||
.west => return robot.readLeft()
|
||||
.west => return robot.readRight()
|
||||
}
|
||||
}
|
||||
fn readEast(self: Mouse) ?bool {
|
||||
switch (self.facing) {
|
||||
.north => return robot.readLeft(),
|
||||
.north => return robot.readRight(),
|
||||
.east => return robot.readFront(),
|
||||
.south => return robot.readRight(),
|
||||
.south => return robot.readLeft(),
|
||||
.west => return null
|
||||
}
|
||||
}
|
||||
fn readSouth(self: Mouse) ?bool {
|
||||
switch (self.facing) {
|
||||
.north => return null,
|
||||
.east => return robot.readLeft(),
|
||||
.east => return robot.readRight(),
|
||||
.south => return robot.readFront(),
|
||||
.west => return robot.readRight()
|
||||
.west => return robot.readLeft()
|
||||
}
|
||||
}
|
||||
fn readWest(self: Mouse) ?bool {
|
||||
switch (self.facing) {
|
||||
.north => return robot.readRight(),
|
||||
.north => return robot.readLeft(),
|
||||
.east => return null,
|
||||
.south => return robot.readLeft(),
|
||||
.south => return robot.readRight(),
|
||||
.west => return robot.readFront()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,102 @@
|
|||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const map = @import("map.zig");
|
||||
|
||||
const stdin = std.io.getStdIn().reader();
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
// Since this only runs on my computer, we can do whatever we want here
|
||||
// const allocator =
|
||||
|
||||
pub fn setup () void {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Sim docs
|
||||
// https://github.com/mackorone/mms?tab=readme-ov-file#example
|
||||
|
||||
fn waitForAck () void {
|
||||
var input = std.BoundedArray(u8, 128).init(0) catch unreachable;
|
||||
_ = stdin.streamUntilDelimiter(input.writer(), '\n', input.capacity()) catch unreachable;
|
||||
assert(std.mem.eql(u8, input.slice(), "ack"));
|
||||
}
|
||||
|
||||
pub fn moveForward() void {
|
||||
// TODO
|
||||
// print "moveForward" to std out
|
||||
_ = stdout.write("moveForward\n") catch unreachable;
|
||||
// wait for "ack" on std in
|
||||
waitForAck();
|
||||
|
||||
// var input = std.BoundedArray(u8, 128).init(0) catch unreachable;
|
||||
// const w = input.writer();
|
||||
// // _ = w.write("hi") catch unreachable;
|
||||
// // std.debug.print("Reading stdin {}\n", .{ input.len });
|
||||
// std.log.debug("Reading std in {}", .{input.capacity()});
|
||||
// _ = stdin.streamUntilDelimiter(w, '\n', input.capacity()) catch unreachable;
|
||||
// std.log.debug("Read {}", .{ input });
|
||||
// std.log.debug("{s}", .{ input.slice()[0..input.len] });
|
||||
// const got: []u8 = input.slice();
|
||||
// std.log.debug("{s}", .{ got });
|
||||
}
|
||||
|
||||
pub fn turn90(direction: map.TurningDirection) void {
|
||||
// TODO
|
||||
_ = direction;
|
||||
_ = stdout.write(switch (direction) {
|
||||
.right => "turnRight\n",
|
||||
.left => "turnLeft\n"
|
||||
}) catch unreachable;
|
||||
|
||||
// wait for "ack" on std in
|
||||
waitForAck();
|
||||
}
|
||||
|
||||
pub fn turn180() void {
|
||||
// TODO
|
||||
turn90(.left);
|
||||
turn90(.left);
|
||||
}
|
||||
|
||||
pub fn stall() noreturn {
|
||||
std.debug.print("Program has completed...", .{});
|
||||
while (true) {
|
||||
|
||||
std.time.sleep(500_000000);
|
||||
std.debug.print(".", .{});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Style all of these
|
||||
pub fn readRight() bool {
|
||||
return true;
|
||||
_ = stdout.write("wallRight\n") catch unreachable;
|
||||
|
||||
var input = std.BoundedArray(u8, 128).init(0) catch unreachable;
|
||||
_ = stdin.streamUntilDelimiter(input.writer(), '\n', input.capacity()) catch unreachable;
|
||||
|
||||
return std.mem.eql(u8, input.slice(), "true");
|
||||
}
|
||||
pub fn readLeft() bool {
|
||||
return true;
|
||||
_ = stdout.write("wallLeft\n") catch unreachable;
|
||||
|
||||
var input = std.BoundedArray(u8, 128).init(0) catch unreachable;
|
||||
_ = stdin.streamUntilDelimiter(input.writer(), '\n', input.capacity()) catch unreachable;
|
||||
|
||||
return std.mem.eql(u8, input.slice(), "true");
|
||||
}
|
||||
pub fn readFront() bool {
|
||||
return true;
|
||||
_ = stdout.write("wallFront\n") catch unreachable;
|
||||
|
||||
var input = std.BoundedArray(u8, 128).init(0) catch unreachable;
|
||||
_ = stdin.streamUntilDelimiter(input.writer(), '\n', input.capacity()) catch unreachable;
|
||||
|
||||
return std.mem.eql(u8, input.slice(), "true");
|
||||
}
|
||||
pub fn readBack() bool {
|
||||
return true;
|
||||
// What?
|
||||
// stdout.write("wallBack\n");
|
||||
|
||||
// var input = std.BoundedArray(u8, 128).init(0) catch unreachable;
|
||||
// _ = stdin.streamUntilDelimiter(input.writer(), '\n', input.capacity()) catch unreachable;
|
||||
|
||||
// return std.mem.eql(u8, input.slice(), "true");
|
||||
std.debug.panic();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// Returns the distance between two integers
|
||||
// Ideally, would take any integer type and
|
||||
// return the same type but unsigned, since this is always positive or 0
|
||||
pub fn intDist (int1: usize, int2: usize) usize {
|
||||
return @max(int1, int2) - @min(int1, int2);
|
||||
}
|
Loading…
Reference in New Issue