Week 12 Tutorial

In this week’s tutorial, we’ll look at working with multiple linked lists, manipulating stacks and queues, some simple recursions, and implementing a simple Final Card-Down player.

Multiple Linked Lists

We’ve seen how to deal with one linked list… what about more?

Stacks and Queues

In lectures, we’ve seen how ADTs allow us to define separate implementations that may radically differ in their approach, but which conform to the same interface.

We saw the Stack ADT again: a stack is a last-in-first-out data structure. We also saw how to implement it with linked lists; here’s Stack.h:

Stack.h
// Will determine how big stackData will be.
#define STACK_SIZE 1000

// An ADT, at last!
typedef struct _stack *Stack;

// create a new Stack
Stack newStack (void);

// destroy a stack
void destroyStack (Stack s);

// this is our add function, 'char elt' = character element.
void stackPush (Stack s, char elt);

// returns the topmost element
char stackTop (Stack s);

// this is our pop function -- takes an element off.
char stackPop (Stack s);

Another simple data structure is a first-in-first-out data structure the queue. The operations on a queue are to enqueue and dequeue a value.

Simple Recursion

Hofstadter’s Law: It always takes longer than you expect, even when you take into account Hofstadter’s Law. — Douglas Hofstadter, Gödel, Escher, Bach: An Eternal Golden Braid

In lectures, we’ve seen the basics of programming recursively: we define a function that has a base case, and a recursive case.


A trivial example of a recursion is to calculate the triangle numbers. The sequence of triangle numbers goes:

Here’s its definition, mathematically.


We can transform some recursive functions into iterations, and some iterative functions into recursions.

We’ve already seen a recursion, and calculated it iteratively:

Playing The Final Card-Down

There are three parts to assignment 2: the first two – to test, and implement, the Game ADT – you’ve already seen and should be working hard on. The third part is to implement an artificial intelligence that plays the game.

We’ve provided a header file, player.h, which looks a lot like this:

player.h
// The player of Final Card-Down. v1.1.0
//
// !!! DO NOT CHANGE THIS FILE !!!

#ifndef PLAYER_H
#define PLAYER_H

#include "Game.h"

// This function is to be implemented by the AI.
// It will be called when the player can take an action on their turn.
// The function is called repeatedly until they make the action
// END_TURN.
// If the player's turn is skipped, this funciton is not called for that
// player.
playerMove decideMove(Game game);

#endif
basicPlayer.c
// An initial A.I. player for Final Card-Down : Tutorial Edition
//
// Written on 2017-10-??
// Tutorial: dayHH-lab (Tutor)
// (e.g. mon13-spoons (Grace Hopper))


#include "Game.h"
#include "player.h"

// The playerMove struct, from the Game.h file:
// typedef struct _playerMove {
//    // Which action to play.
//    action action;
//    // Declare which color must be played on the next turn.
//    // This is only used when playing a DECLARE.
//    color nextColor;
//    // Which card to play (only valid for PLAY_CARD).
//    Card card;
// } playerMove;

// This function is to be implemented by the A.I.
// It will be called when the player can make an action on their turn.
//
// !!!!!   The function is called repeatedly, until   !!!!!
// !!!!!      they make the action `END_TURN`.        !!!!!
//
// If the player's turn is skipped, this funciton
// is *not* called for that player.
//
// Don't forget about the `isValidMove` function -- it's a handy way
// to work out before you play a move whether or not it will be valid
// (and you should only ever be making valid moves).
//
// You can also use it to help you work out where you are at in the
// game, without needing to look through all of the previous state
// yourself --
//
// Looking at the diagram of valid moves at any given point in the game,
// we can see that at the start of the game, it's valid to:
//   - call somebody out for forgetting to SAY_UNO / SAY_DUO / SAY_TRIO,
//   - draw a card,
//   - play a card, *if* you have a valid card that you can play.
//
// It's not valid to end your turn unless you've done some other
// action/s (again, see the diagram).
//
// We can take advantage of that for our very simple A.I. to determine
// where we are at in our turn, and thus what move we should make.

playerMove decideMove(Game game) {

    // Start out by making a move struct, to say what our move is.
    playerMove move;

    // Set our move to be END_TURN, and check whether that's
    // a valid move -- if it is, then just end our turn (for now).
    move.action = END_TURN;

    // If it's not valid to end turn, we must need to make
    // some other action...
    //
    // What actions are valid at this point?
    if (!isValidMove(move)) {
        move.action = // ?????
    }

    return move;
}

You need to work with your lab partner to implement a player.c; in the lab today, you should get started on that.

In the next competition rounds, we’ll run games with all the submitted players, so you should submit a compiling player.

Next Week…

… is our last week! Have a party!

(… and maybe think about the various things we’ve learned over the course of this semester!)