Snail Gate Project

by: burt rosenberg
at: september 2024

Overview

This project is a fun project with three serious goals,

  1. To introduce thread programming,
  2. To show how condition-wait works,
  3. To show the techniques of data sharing between threads in a process.
A draw thread is launched, which loops at a certain rate, incrementing the snail and drawing a picture of it, and a gate. The gate is either open and closed. If the gate is open when the snail arrives it passes through and eventually the game is won. Else the snail smushes against the gate and the game is lost.

The Snail Gate Game

The nag program

Man page

NAME
   snail --- the snail gate game
      
SYNOPSIS

    snail [-v]

DESCRIPTION

   As the snail moves towards a gate, the user guesses the one digit key to the 
   door. If the user guesses the key, the door opens (depicted by the ? in the 
   gate disappearing) and the snail can pass through the gate.
 
OPTIONS

    -v verbose
     
ARGUMENTS

    None

OUTPUT
  
    A graphic with a snail @/, an closed gate [?] and an open gate [ ].
    
HISTORY

    New for csc421-231.

    

The nag program, github://csc-courses/csc421/nag.c, is the basis for this project. The nag routine is a thread that from time to time wakes up and checks if something new has been said. If not it nags the user to say something. However if so, it insults the user for what they said.

Note in this program,

  1. The thread is launched in the main route with pthread_create().
  2. The main and nag threads communicate through a struct T_arg, passed to the nag thread on creation.
  3. Use of data in common between the nag and main thread is synchronized by being placed inside a block beginning with pthread_mutex_lock() and ending with pthread_mutex_unlock().
  4. The nag thread waits by calling pthread_cond_timedwait(), taking three arguments,
    1. The mutex to wait on, of type pthread_mutex_t,
    2. The condition signal to listen for, of type pthread_cond_t,
    3. A timer value for the time-out, struct timespec.
  5. The main thread can signal thread nag thread on the pthread_cond_t by calling pthread_cond_signal().
  6. The nag thread knows whether the shared buffer data is new or old by comparing a counter that is set by the main thread with an older value that is saved by the nag thread.
  7. The exit is asked for by the main by way of a request variable, req_exit, and the effective acknowledgement is by the main waiting for the thread's call to pthread_exit by its own call to pthread_exit.
For more on Pthreads, see LLNL's documentation.

Data structures

The snail gate program operates very similar to the nag program. It is on the technical level the nag program, but with a different story line.

Snail gate runs with two threads. The main thread and the draw_thread thread.

The main thread initializes the data structures and then creates the draw thread, exactly as nag created the nag thread.

The two threads communicate with two shared data structures using two different techniques,

  1. The common reference to (struct Board) board_g, is shared as being a global;
  2. or (struct T_arg) t_arg which is allocated on the stack as a local variable of main, is shared by being the argument passed to draw_thread.

 struct Board {
	int snail_loc ;
	char guess ;
	int gate_loc ;
	int gate_key ;
	int gate_state ;
	} ;

struct T_arg {
 	int req_exit ;
 	pthread_mutex_t * mutex ;
 	pthread_cond_t * cond  ;
 	} ;

The board information is held in a global struct of type struct Board. The snail has advanced to integer position snail_loc. The main thread update guess with what the user has guessed. The get is located at gate_loc and is opened by the character gate_key. The gate_key takes one of two values, GATE_IS_CLOSED or GATE_IS_OPEN. This field is used when creating the string tha represents the game.

The thread argument is a structure bundling up the shared lock, (pthread_mutex_t) * mutex and the shared condition variable (pthread_cond_t) * cond as well as a flag req_exit which is set to request the draw thread to exit.

Note that draw_thread formally declares the argument to be a void *. Review the meaning of a void-star. TLDR; version — you will need to cast it to its actually type struct T_arg *.

snail.c

The task of the draw thread is to loop, sleeping on either a timeout or a wakeup signal from the main thread.

When the draw thread is awakened,

  1. The snail advances by one;
  2. The snail is checked for either getting smushed or game over;
  3. The latest guess his checked and recorded as the gate opened or closed;
  4. If the thread is being asked to exit, this is handled.
The main thread is the controller thread, and is waiting for input. If it gets input it wakes the draw thread to handle the input. There are two types of input: to quit, or a new guess.

snail-util.c

You have been provided with utility functions to draw the game board. In the discipline of Model-View-Controller, the main thread is the Controller and the draw thread is the Viewer. The Model is latent in the data structures, which are shared between viewer and controller.

Makefile

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

author: burton rosenberg
created: 20 sep 2022
update: 29 sep 2024