8-puzzle in rust
I think rust is one of the most interesting upcoming programming languages out there. So I wrote a solution to the 8-puzzle (see the 15-puzzle) using A*. It also has a breadth first search for solutions on a specific distance away from the goal.
The solution is not by any means a great one and there are parts I’m a bit annoyed with, but that’s mostly because of my inexperience with rust.
Rust was surprisingly easy to use and I like the strong typing and the pattern matching. One feature I found very useful was to do println!("{:?}", x);
and be able to print any variable, composite or not.
I’m not friends with the different storage containers and especially the moving of values. Often I called a function with a value, but then I couldn’t reuse the value in another, so I ended up using clone()
a bunch. I’m probably abusing it and there’s bound to be a better way to do things.
I got a little stumped on the standard library and spent some time perusing the documentation. It was very light on examples, which I find is the by far most useful thing to have in documentation, and I couldn’t even find some of the things. But it all worked out with a bit of determination.
I should mention that I’m using rust master, not a stable release, and I’m well aware of rust being in development at the moment. Still things worked out fine and I quite enjoyed dabbling with rust. I’ll write some more in it I think.
This is the code, working on rustc 0.10-pre (5512fb4 2014-01-19 05:56:35 -0800)
:
/**
* A solution to the 8-tiles puzzle, implemented in rust
* as a learning experience.
*/
extern
use abs;
use HashMap;
use HashSet;
use PriorityQueue;
use Deque;
use RingBuf;
// Use manhattan distance as heuristic for A*.
// Sum of distance in x and distance in y for all tiles.
let mut dist: uint = 0;
for i in range for j in range if start == dest let x1 = i as int % 3;
let y1 = i as int / 3;
let x2 = j as int % 3;
let y2 = j as int / 3;
dist += abs as uint;
dist += abs as uint;
// Avoid double counting so we don't overestimate.
dist / 2
~str let mut hash = ~"";
for x in state.iter hash.push_str;
hash
for i in range for j in range print!;
println!;
for i in range for j in range print!;
if i == 1
else
for j in range print!;
println!;
// A* uses g: cost so far, h: manhattan distance to goal, f: g + h
state: ~[int],
g: uint,
h: uint,
zero: uint,
let h = manhattan;
State
// Ignore h and f.
State
println!;
print_state;
self.f > s2.f
static dx: = ;
static dy: = ;
// Calculate the minimum distance from a state to goal with A*
let mut dist = new;
let mut q = new;
dist.insert;
let zero = match state.position_elem Some => x,
None => fail!,
;
q.push;
loop // Ugly, but don't know how...
let = match q.maybe_top Some => ,
None => break,
;
q.pop;
if state == goal
let x = pos % 3;
let y = pos / 3;
for d in range let nx = x + dx;
let ny = y + dy;
if nx < 0 || nx >= 3 || ny < 0 || ny >= 3
let npos = nx + 3 * ny;
let mut next_state = state.clone;
next_state= next_state;
next_state= 0;
let next_hash = to_hash;
let next_cost = cost + 1;
let s = new;
if s.state == goal
if !dist.contains_key || s.f < *dist.get dist.insert;
q.push;
0u // Should never happen!
// Calculate the number of solutions with optimal solution length = target_dist.
// Use breadth first.
let mut count = 0u;
let mut seen = new;
let mut q = new;
seen.insert;
let zero = match goal.position_elem Some => x,
None => fail!,
;
q.push_back;
loop // Ugly, but don't know how...
let = match q.front Some => ,
None => break,
;
q.pop_front;
if dist > target_dist
else if dist == target_dist
let x = pos % 3;
let y = pos / 3;
for d in range let nx = x + dx;
let ny = y + dy;
if nx < 0 || nx >= 3 || ny < 0 || ny >= 3
let npos = nx + 3 * ny;
let mut next_state = state.clone;
next_state= next_state;
next_state= 0;
let next_hash = to_hash;
let next_dist = dist + 1;
let s = new_simple;
if !seen.contains seen.insert;
q.push_back;
count
let state = ~;
let goal = ~;
print;
let sol = dist;
println!;
let target_dist = sol;
println!;