* This is your template code for a simple maze “game”. The player’s task

#define NODEBUG
* This is your template code for a simple maze “game”. The player’s task
* is to drag an arrow through the maze from the red starting dot to touch
* the Blue middle square without touching the black maze walls on the way.
* The game starts when you drag across the red starting dot. The arrow you
* are dragging is red if you have not yet touched the starting dot or if
* you’ve already won or lost. It turns blue to show when the game is
* running and to indicate that your job is to get to the blue square.
* If you touch the maze walls or release your button press (this is
* to prevent cheating by skipping over walls) the arrow becomes red
* to show that you have lost the game and need to start again.
* Its NOT as easy as it seems especially if you have a laptop trackpad!!!
* By the way, the maze came from …
* http://commons.wikimedia.org/wiki/Image:Triple-Spiral-labyrinth.svg
* I added the blue square and red starting dot, and made the background
* greeen. The resulting ppm image files that you need are
* http://staff.psc.edu/awetzel/mymaze.ppm or a much easier one called
* http://staff.psc.edu/awetzel/easymaze.ppm
* Your assignment of course is to fill out this template to reimplement
* some of the things that are removed from my working version. These
* are marked in the code at the comment positions “XXX Your code here”
* The particular tasks are
* 1. test the RGB image value corresponding to the current mouse position
* 2. compute the estimated direction of current motion
* 3. “manually” rotate and position the arrow in the direction of motion
* 4. print some additional text on the screen to count redraw cycles
* 5. design a new arrow shape and make it work properly
* 6. fix the game ending with something to better distinguish a win
* 7. EXTRA CREDIT – any other SIGNIFICANT improvement of your own design
* The specific aims of these tasks is to 1) reinforce your understanding
* of what an image is and how its laid out in memory. 2+3) refresh your
* understanding of elementary trig which will be very important later
* 4) show you how to position and print bitmap text 5) understand 2D
* shapes and their manipulation 6+7) exercise your creativity
* Before you change anything the program should already successfully
* show the maze image but not do anything except handle the ESCape key.
* Please email your completed solution code to me before the next class.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <GL/glut.h>
#ifndef M_PI // Microsoft forgot to put this in their math.h !
#define M_PI 3.14159265358979

void init(int argc, char **argv);
void init_glut(int argc, char **argv);
void init_gl(int argc, char **argv);
void reshape(int w, int h);
void display(void);
void keyboard(unsigned char key, int x, int y);
void mouse_buttons(int button, int state, int mousex, int mousey);
void mouse_motion(int mousex, int mousey);

int arrowx = 0, arrowy = 0;
float arrow_lastx = 0, arrow_lasty = 0; // floats to do a lagged average
int gamerunning;

int ppm_wid, ppm_ht, ppm_range;
unsigned char *pixels;

int main(int argc, char **argv) {
init(argc, argv);
init_glut(argc, argv);
init_gl(argc, argv);

// initialize, in this case, means read the maze image file
// WARNING: this does not handle PPM header comments and other variations!!!
void init(int argc, char **argv) {
FILE *fp;
char tmptext[100], *fname = “easymaze.ppm”;

if(!(fp = fopen(fname, “rb”))) { // open binary file for read
fprintf(stderr, “ERROR: can’t open %sn”, fname);
fname = “tinymaze.ppm”;
if(!(fp = fopen(fname, “rb”))) {
fprintf(stderr, “ERROR: can’t open %sn”, fname);
fscanf (fp, “%[^n] “, tmptext);
fprintf(stderr, “file magic string <%s>n”, tmptext);
if(strcmp(tmptext, “P6”)) {
fprintf(stderr, “Sorry: this only does P6 == PPMn”);

fscanf (fp, “%d%d”, &ppm_wid, &ppm_ht);
fprintf(stderr, “ppm image size %d X by %d Yn”, ppm_wid, ppm_ht);

fscanf(fp, “%dn”, &ppm_range);
if(ppm_range != 255) {
fprintf(stderr, “Sorry: range %d is not allowedn”, ppm_range);

pixels = (unsigned char *)malloc(ppm_ht*ppm_wid*3);
if(!pixels) {
fprintf(stderr, “malloc failed on image size %d %dn”,
ppm_ht, ppm_wid);
fread(pixels, 1, ppm_ht*ppm_wid*3, fp);

void init_glut(int argc, char **argv) {
glutInit(&argc, argv);
glutInitWindowPosition(300, 200); // a reasonable screen XY ???
glutInitWindowSize(ppm_wid, ppm_ht); // window size same as image
//glutFullScreen(); // if you want to experiment
glutCreateWindow(“Assignment 02”);
glutDisplayFunc(display); // this does the 1st drawing
glutMouseFunc(mouse_buttons); // this catches mouse buttons
glutMotionFunc(mouse_motion); // this works even if no button pressed
void init_gl(int argc, char **argv) {
glClearColor(1, 1, 1, 1);

void reshape(int w, int h) {
glViewport(0, 0, w, h);
glOrtho(0, ppm_wid, 0, ppm_ht, 1, 100);
gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0);

void keyboard(unsigned char key, int x, int y) {
switch(key) {
case 27: // escape key
fprintf(stderr, “unknown key %d at %d %dn”, key, x, y);

void draw_string(float x, float y, char *s) {
glColor3f(0.0, 0.0, 0.0);
glRasterPos2f(x, y) ; // place left baseline text start position
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *s++);

int mouse_saved_button, mouse_saved_state; // useful in mouse_motion()
void mouse_buttons(int button, int state, int mousex, int mousey) {
mouse_saved_button = button;
mouse_saved_state = state;
switch(button) {
if(state == GLUT_DOWN) {
//fprintf(stderr, “just pressed %d %dn”, mousex, mousey); // help debug
if(state == GLUT_UP) {
if(gamerunning == 1) { // may change this for debug
fprintf(stderr, “No button lifting!!!n”);
gamerunning = -1; // Don’t cheat!!!
//fprintf(stderr, “just released %d %dn”, mousex, mousey); // help debug

void mouse_motion(int mousex, int mousey) {
int R, G, B;
unsigned char *p;
switch(mouse_saved_button) {
//fprintf(stderr, “%f %f %d %d %d %dn”,
//arrow_lastx, arrow_lasty, arrowx, arrowy, mousex, mousey);
arrow_lastx = 0.7 * arrow_lastx + 0.3 * arrowx;
arrow_lasty = 0.7 * arrow_lasty + 0.3 * arrowy;
arrowx = mousex;
arrowy = mousey;
if(mouse_saved_state == GLUT_DOWN) {
// 1) XXX Your code retrieve the RGB of cursor pixel.
// First make the pointer p point to the R value
// for the current pixel inside the pixels array.
// Then successively retrive the R, G, B values.
// Its about 4 lines of code.
#ifdef DEBUG
fprintf(stderr, “GLUT_DOWN at %d %d RGB %d %d %d %dn”,
mousex, mousey, R, G, B, gamerunning);
// 6) XXX Your code to make the ending more dramatic
// make the screen flash or something else in the
// following if else sections
if(R == 255) {
if(gamerunning == 0)
fprintf(stderr, “Game started!n”);
gamerunning = 1;
} else if(gamerunning == 1 && B > 10) {
fprintf(stderr, “You Won :-)n”);
gamerunning = 2;
} else if(gamerunning == 1 && G < 100) {
fprintf(stderr, “Gameover, You Lost :-(n”);
gamerunning = -1;
glutPostRedisplay(); // something moved so redisplay here

// 5) XXX Your code to make a new and improved arrow shape
#define NPTS 4 // there are 4 points in this arrowhead shape
// the ashape array contains the original shape XY coordinate pairs
float ashape[NPTS][2] = { 0.0, 0.0, 24.0, -10.0, 20.0, 0, 24.0, 10.0 };

void display(void) {
char text[100]; // for holding screen print text
float rev_y, cosdir, sindir;
float rshape[NPTS][2]; // transformed coordinates for the shape
float dx, dy, hyp; // current motion and its right triangle hypotenuse
int i;
static int n_display = 0;

glPixelZoom(1, -1); // “Proper” orientation for pixel ops
glRasterPos2f(0.0, ppm_ht); // XY position to draw the maze
// now put the maze image into the frame buffer (try leaving this out)
glDrawPixels(ppm_wid, ppm_ht, GL_RGB, GL_UNSIGNED_BYTE, pixels);

// 4) XXX Your code to print a display cycle counter in lower left
// about 3 lines – look back to GLBase.c

switch(gamerunning) {
case -1:
draw_string(10.0, ppm_ht – 20.0, “You Lost :-(“);
case 0:
draw_string(10.0, ppm_ht – 20.0, “Start on Red Circle”);
case 1:
draw_string(10.0, ppm_ht – 20.0, “GAME Running”);
sprintf(text, “X = %3d Y = %3d”, arrowx, arrowy);
draw_string(10.0, ppm_ht – 40.0, text);
case 2:
draw_string(10.0, ppm_ht – 20.0, “You Won :-)”);

// 2) XXX Your code to compute cosdir and sindir from the values
// of the current and last arrow positions (arrowx, arrow_lastx,
// arrowy, arrow_lasty … BEWARE arrow_lastxy are floats
// you should first compute dx, dy and hyp then sindir and cosdir
// about 5 lines of code.
#ifdef DEBUG
fprintf(stderr, “%d %g %d %g %g %g %g %g %gn”,
arrowx, arrow_lastx, arrowy, arrow_lasty, dx, dy, hyp, sindir, cosdir);
rev_y = ppm_ht – arrowy – 1; // rev_y is just Y flipped
for(i = 0; i < NPTS; i++) {
// 3) XXX Your code to produce properly transformed 2D
// shape coordinates in the rshape array by using the
// current arrow position, the ashape values and the
// sindir, cosdir values you computed for item 2)
// Two to four lines depending on your spacing.
// Make the color coded filled arrow shape
if(gamerunning == 1)
glColor3ub(0, 0, 255);
glColor3ub(255, 0, 0);
for(i = 0; i < NPTS; i++)
// It looks nicer with a black outline
glColor3ub(0, 0, 0);
for(i = 0; i < NPTS; i++)

// 7) XXX Your extra credit code could go anywhere