Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
packetpirate committed Apr 27, 2015
1 parent 7c8fb29 commit ed9a2bd
Showing 1 changed file with 63 additions and 34 deletions.
97 changes: 63 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
# Tune-Traveler
##Authors
Darin Beaudreau

Xiaoyang Li

ChoChak Wong

##Overview
Tune Traveler is the final project for the Spring 2015 Organization of Programming Languages class.
The idea behind the project was to implement the A* path-finding algorithm visually and then, as a
little something extra, give it some sound. We wanted the algorithm to generate a "song" as it traverses
the nodes in the grid by playing a random sound every time it moves from node to node.

Authors: Darin Beaudreau, Xiaoyang Li, ChoChak Wong

## What is A*?
The A* path-finding algorithm is widely used in video game development as a means of finding the shortest path from point A to B -- that is, it is used to find a path that can be traveled to reach the destination in the least amount of movement. Chances are, if you've played a game where an object in a game found its way through a maze, moved to a position in a certain way, or anything like that, it was the A* algorithm being used.

## How does it work? (thinky words ahead)
A* is a slightly fancier version of Djikstra's Algorithm, another path-finding algorithm taught in Discrete Structures. The difference between the two being that A* uses a heuristic search, where Djikstra's lacks this component.

What it all boils down to is F = G + H. Each node in the grid is assigned an F score, which is the "cost" of moving to that node. The G score is calculated by simply determining whether moving to that node would require a horizontal/vertical movement, or a diagonal movement. If it is horizontal or vertical, a value of 10 is assigned (or 1, doesn't really matter, as long as everything is consistent). If the movement is diagonal, the value 14 is assigned. This is because moving from one node to a node to the left or right, for example, only requires moving 1 space. The distance between the current node and a node that is diagonal from it would require a number of spaces of movement equal to the square root of 2. In decimal, that is 1.4141414... so both numbers are multiplied by 10 to get 10 and 14.

The H score represents the vertical and horizontal offset from the goal multiplied by 10. If you remember rise over run from algebra, you'll understand. The H score is the sum of these two offsets multiplied by 10.

Adding these two scores together gives you the "cost" of moving to that node. Then, the algorithm simply chooses the path of which has the least F cost. So the path with the least sum of F scores is the path it takes.

## What does it look like in action?
I'm glad you asked that, because what is a project demonstration without pretty pictures to hold the reader's attention? Here are a few "mazes" that have been solved by the algorithm.
##Screenshot
Here are a few "mazes" that have been solved by the algorithm.

**A diagram of how the algorithm steps through the nodes in the grid and chooses the least F score.**

Expand All @@ -37,21 +30,57 @@ I'm glad you asked that, because what is a project demonstration without pretty

![Maze with No Solution](images/tune-traveler_05.png)

## How can I play with it?
Simply fork/clone this repository, or download the ZIP, to get the files. Then, open "tune-traveler.rkt" in DrRacket. Assuming you have the RSound library installed, you're good to go. Just hit Run and watch!

## Can I make my own levels?
You sure can. Just create a text file with 15 rows of 15 characters. If you don't know what I mean by this, look at the levels that come with this repository. It is a good idea to put a border around the maze, though it is not required. Here is a key of the characters used.

1 - Wall

0 - Blank/Nothing

S - Starting position.

E - End position.

Then go to "levels.rkt" and add the level to the "maps" list in the same way that the others were added and in "tune-traveler.rkt", change the "LEVEL" variable to the list index for your level (if your level is the 5th in the list, change it to 4).
##Concepts Demonstrated
A* is a slightly fancier version of Djikstra's Algorithm, another path-finding algorithm taught in Discrete Structures. The difference between the two being that A* uses a heuristic search, where Djikstra's lacks this component.

And that should do it!
You can also edit the "GRID_SIZE" variable in "constants.rkt" if you want to make larger levels. Just make sure you don't try to load a level of a different size.
Some of the concepts from OPL that are demonstrated in my project are...

- Extensive use of maps and lambda procedures were used.
- Data abstraction was demonstrated with the tile class.

Most of what was used in this project was adapted from other languages to use a functional approach. It was a challenge to think about the A* algorithm, as well as all the other concepts I thought I was familiar with using Racket.

##External Technology and Libraries
The external libraries that were utilized were OpenGL and RSound. OpenGL was used to render the grid and the graphical representation of the maze to the screen, as well as to animate the "player" moving from node to node.

RSound was used to play the random sounds as the player traverses each node.

##Favorite Lines of Code
####Darin

My favorite piece of code was the A* algorithm itself. I felt that it came out looking fairly straightforward. While I think it could have been optimized a bit more through condensing it into separate functions, I didn't find it necessary since none of the code was repeated.

```scheme
; Define the A* search function.
(define (search GRID A B)
(let ([open nil] ; The open list, which contains tiles for the algorithm to consider as it walks through the "maze".
[closed nil] ; The closed list, which contains tiles that have already been considered (traversed) and can be ignored.
[current nil] ; The current tile.
[neighbors nil]) ; A list containing the neighbors of the current tile.
(set! open (append open (list A))) ; Add the start tile to the open list.
(define (searchLoop)
(begin (set! current (lowestF open)) ; Find the tile in the open list with the lowest F score.
(set! closed (append closed (list current))) ; Add the current tile to the closed list since we're done "exploring" it.
(set! open (remove current open)) ; Remove the current tile from the open list so we don't accidentally "explore" it again.
(unless (sameTile? current B) ; Unless this is the goal tile, keep searching. Otherwise, we're done here.
(begin (set! neighbors (getNeighbors GRID current)) ; Retrieve the 8 neighbor tiles surrounding the current tile. Un-walkable tiles excluded.
(map (lambda (t) ; Map over each neighbor tile...
(unless (member t closed) ; Ignore tiles that are on the closed list... we've already "explored" them.
(when (not (member t open)) ; If the neighbor is not in the open list...
(begin (send t setG (compG current t)) ; Compute the G score of the neighbor.
(send t setH (compH t B)) ; Compute the H score of the neighbor.
(send t setF (compF (send t getG) (send t getH))) ; Compute the F score of the neighbor.
(send t setParent current) ; Set the parent of the neighbor to the current tile.
(set! open (append open (list t))))))) ; Add the neighbor to the open list.
neighbors)
(unless (empty? open) (searchLoop)))))) ; If there are no more tiles in the open list, we're done searching.
(searchLoop) ; Call the search procedure's main loop.
(retrace GRID A current))) ; Retrace the steps from goal to start to find the path that the "player" takes.
```

##Additional Remarks
#### Darin
I had a blast learning about the A* algorithm. Towards the end, I had a lot of trouble getting it to just run, but through perserverance and patience, I was able to implement an algorithm I've been wanting to explore for years.

#How to Download and Run
Simply fork/clone this repository, or download the ZIP, to get the files. Then, open "tune-traveler.rkt" in DrRacket. Assuming you have the RSound library installed, you're good to go. Just hit Run and watch!

0 comments on commit ed9a2bd

Please sign in to comment.