diff --git a/assignment2_handout.c b/assignment2_handout.c new file mode 100644 index 0000000..221bc13 --- /dev/null +++ b/assignment2_handout.c @@ -0,0 +1,356 @@ +// -------------------------------------------------- +// --- 159.341 Assignment 2 - Lift Simulator --- +// -------------------------------------------------- +// Written by M. J. Johnson +// Updated by D. P. Playne - 2019 +#include +#include +#include +#include "lift.h" + +// -------------------------------------------------- +// Define Problem Size +// -------------------------------------------------- +#define NLIFTS 4 // The number of lifts in the building +#define NFLOORS 20 // The number of floors in the building +#define NPEOPLE 20 // The number of people in the building +#define MAXNOINLIFT 10 // Maximum number of people in a lift + + +// -------------------------------------------------- +// Define delay times (in milliseconds) +// -------------------------------------------------- +#define SLOW +// #define FAST + +#if defined(SLOW) + #define LIFTSPEED 50 // The time it takes for the lift to move one floor + #define GETINSPEED 50 // The time it takes to get into the lift + #define GETOUTSPEED 50 // The time it takes to get out of the lift + #define PEOPLESPEED 100 // The maximum time a person spends on a floor +#elif defined(FAST) + #define LIFTSPEED 0 // The time it takes for the lift to move one floor + #define GETINSPEED 0 // The time it takes to get into the lift + #define GETOUTSPEED 0 // The time it takes to get out of the lift + #define PEOPLESPEED 1 // The maximum time a person spends on a floor +#endif + +// -------------------------------------------------- +// Define lift directions (UP/DOWN) +// -------------------------------------------------- +#define UP 1 +#define DOWN -1 + +// -------------------------------------------------- +// Information about a floor in the building +// -------------------------------------------------- +typedef struct { + int waitingtogoup; // The number of people waiting to go up + int waitingtogodown; // The number of people waiting to go down + semaphore up_arrow; // People going up wait on this + semaphore down_arrow; // People going down wait on this +} floor_info; + +// -------------------------------------------------- +// Information about a lift +// -------------------------------------------------- +typedef struct { + int no; // The lift number (id) + int position; // The floor it is on + int direction; // Which direction it is going (UP/DOWN) + int peopleinlift; // The number of people in the lift + int stops[NFLOORS]; // How many people are going to each floor + semaphore stopsem[NFLOORS]; // People in the lift wait on one of these +} lift_info; + +// -------------------------------------------------- +// Some global variables +// -------------------------------------------------- +floor_info floors[NFLOORS]; + +// -------------------------------------------------- +// Print a string on the screen at position (x,y) +// -------------------------------------------------- +void print_at_xy(int x, int y, const char *s) { + // Move cursor to (x,y) + gotoxy(x,y); + + // Slow things down + Sleep(1); + + // Print the string + printf("%s", s); + + // Move cursor out of the way + gotoxy(42, NFLOORS+2); +} + +// -------------------------------------------------- +// Function for a lift to pick people waiting on a +// floor going in a certain direction +// -------------------------------------------------- +void get_into_lift(lift_info *lift, int direction) { + // Local variables + int *waiting; + semaphore *s; + + // Check lift direction + if(direction==UP) { + // Use up_arrow semaphore + s = &floors[lift->position].up_arrow; + + // Number of people waiting to go up + waiting = &floors[lift->position].waitingtogoup; + } else { + // Use down_arrow semaphore + s = &floors[lift->position].down_arrow; + + // Number of people waiting to go down + waiting = &floors[lift->position].waitingtogodown; + } + + // For all the people waiting + while(*waiting) { + // Check if lift is empty + if(lift->peopleinlift == 0) { + // Set the direction of the lift + lift->direction = direction; + } + + // Check there are people waiting and lift isn't full + if(lift->peopleinlift < MAXNOINLIFT && *waiting) { + // Add person to the lift + lift->peopleinlift++; + + // Erase the person from the waiting queue + print_at_xy(NLIFTS*4+floors[lift->position].waitingtogodown + floors[lift->position].waitingtogoup, NFLOORS-lift->position, " "); + + // One less person waiting + (*waiting)--; + + // Wait for person to get into lift + Sleep(GETINSPEED); + + // Set lift to enter + // Signal passenger to enter + } else { + break; + } + } +} + +// -------------------------------------------------- +// Function for the Lift Threads +// -------------------------------------------------- +void* lift_thread(void *p) { + // Local variables + lift_info lift; + int no = (long long)p; + int i; + + // Set up Lift + lift.no = no; // Set lift number + lift.position = 0; // Lift starts on ground floor + lift.direction = UP; // Lift starts going up + lift.peopleinlift = 0; // Lift starts empty + + for(i = 0; i < NFLOORS; i++) { + lift.stops[i]=0; // No passengers waiting + semaphore_create(&lift.stopsem[i], 0); // Initialise semaphores + } + + // Randomise lift + randomise(); + + // Wait for random start up time (up to a second) + Sleep(rnd(1000)); + + // Loop forever + while(TRUE) { + // Print current position of the lift + print_at_xy(no*4+1, NFLOORS-lift.position, lf); + + // Wait for a while + Sleep(LIFTSPEED); + + // Drop off passengers on this floor + while (lift.stops[lift.position] != 0) { + // One less passenger in lift + lift.peopleinlift--; + + // One less waiting to get off at this floor + lift.stops[lift.position]--; + + // Wait for exit lift delay + Sleep(GETOUTSPEED); + // Signal passenger to leave lift + + // Check if that was the last passenger waiting for this floor + if(!lift.stops[lift.position]) { + // Clear the "-" + print_at_xy(no*4+1+2, NFLOORS-lift.position, " "); + } + } + // Check if lift is going up or is empty + if(lift.direction==UP || !lift.peopleinlift) { + // Pick up passengers waiting to go up + get_into_lift(&lift, UP); + } + // Check if lift is going down or is empty + if(lift.direction==DOWN || !lift.peopleinlift) { + // Pick up passengers waiting to go down + get_into_lift(&lift, DOWN); + } + + // Erase lift from screen + print_at_xy(no*4+1, NFLOORS-lift.position, (lift.direction + 1 ? " " : lc)); + + // Move lift + lift.position += lift.direction; + + // Check if lift is at top or bottom + if(lift.position == 0 || lift.position == NFLOORS-1) { + // Change lift direction + lift.direction = -lift.direction; + } + } + + return NULL; +} + +// -------------------------------------------------- +// Function for the Person Threads +// -------------------------------------------------- +void* person_thread(void *p) { + // Local variables + int from = 0, to; // Start on the ground floor + lift_info *lift; + + // Randomise + randomise(); + + // Stay in the building forever + while(1) { + // Work for a while + Sleep(rnd(PEOPLESPEED)); + do { + // Randomly pick another floor to go to + to = rnd(NFLOORS); + } while(to == from); + + // Check which direction the person is going (UP/DOWN) + if(to > from) { + // One more person waiting to go up + floors[from].waitingtogoup++; + + // Print person waiting + print_at_xy(NLIFTS*4+ floors[from].waitingtogoup +floors[from].waitingtogodown,NFLOORS-from, pr); + + // Wait for a lift to arrive (going up) + } else { + // One more person waiting to go down + floors[from].waitingtogodown++; + + // Print person waiting + print_at_xy(NLIFTS*4+floors[from].waitingtogodown+floors[from].waitingtogoup,NFLOORS-from, pr); + + // Wait for a lift to arrive (going down) + } + + // Which lift we are getting into + lift = 0; + + // Add one to passengers waiting for floor + lift->stops[to]++; + + // Press button if we are the first + if(lift->stops[to]==1) { + // Print light for destination + print_at_xy(lift->no*4+1+2, NFLOORS-to, "-"); + } + + // Wait until we are at the right floor + + // Exit the lift + from = to; + } + + return NULL; +} + +// -------------------------------------------------- +// Print the building on the screen +// -------------------------------------------------- +void printbuilding(void) { + // Local variables + int l,f; + + // Clear Screen + system(clear_screen); + + // Print Roof + printf("%s", tl); + for(l = 0; l < NLIFTS-1; l++) { + printf("%s%s%s%s", hl, td, hl, td); + } + printf("%s%s%s%s\n", hl, td, hl, tr); + + // Print Floors and Lifts + for(f = NFLOORS-1; f >= 0; f--) { + for(l = 0; l < NLIFTS; l++) { + printf("%s%s%s ", vl, lc, vl); + if(l == NLIFTS-1) { + printf("%s\n", vl); + } + } + } + + // Print Ground + printf("%s", bl); + for(l = 0; l < NLIFTS-1; l++) { + printf("%s%s%s%s", hl, tu, hl, tu); + } + printf("%s%s%s%s\n", hl, tu, hl, br); + + // Print Message + printf("Lift Simulation - Press CTRL-Break to exit\n"); +} + +// -------------------------------------------------- +// Main starts the threads and then waits. +// -------------------------------------------------- +int main() { + // Local variables + unsigned long i; + + // Initialise Building + for(i = 0; i < NFLOORS; i++) { + // Initialise Floor + floors[i].waitingtogoup = 0; + floors[i].waitingtogodown = 0; + semaphore_create(&floors[i].up_arrow, 0); + semaphore_create(&floors[i].down_arrow, 0); + } + + // --- Initialise any other semaphores --- + + // Print Building + printbuilding(); + + // Create Lifts + for(i = 0; i < NLIFTS; i++) { + // Create Lift Thread + create_thread(lift_thread, (void*)i); + } + + // Create People + for(i = 0; i < NPEOPLE; i++) { + // Create Person Thread + create_thread(person_thread, (void*)i); + } + + // Go to sleep for 86400 seconds (one day) + Sleep(86400000ULL); +} + + diff --git a/lift.h b/lift.h new file mode 100644 index 0000000..165afb1 --- /dev/null +++ b/lift.h @@ -0,0 +1,167 @@ +#ifndef LIFT_H_ +#define LIFT_H_ + +// ------------------------------ +// Random Number Generator +// ------------------------------ +// Return a random number from 0..i-1 (inclusive) +#define rnd(i) (rand()%(i)) +int rand_seed = 123456; +#define randomise() srand(rand_seed);rand_seed=rand(); + +// ------------------------------ +// Move cursor to (x,y) +// ------------------------------ +void gotoxy(int x,int y) { + printf("%c[%d;%df",0x1B,y+1,x+1);fflush(stdout); +} + +// ------------------------------ +// Windows Specific Code +// ------------------------------ +#if defined(_WIN32) || defined(WIN32) +#include +#define semaphore HANDLE + +// Wait for a semaphore +void semaphore_wait(semaphore *s) { + // Sem wait + WaitForSingleObject(*s, MAXLONG); +} + +// Signal a semaphore +void semaphore_signal(semaphore *s) { + // Sem post + ReleaseSemaphore(*s, 1, NULL); +} + +// Create a semaphore initialised to value +void semaphore_create(semaphore *s, int value) { + // Initialise Semaphore + (*s) = CreateSemaphore(NULL, (long)value, MAXLONG, NULL); +} + +// Create a Thread +void create_thread(void* (*func)(void*), void *args) { + // Create Thread - Windows API + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, args, 0, NULL); +} + +// Constants for printing +const char *tl = "\xc9"; +const char *tr = "\xbb"; +const char *bl = "\xc8"; +const char *br = "\xbc"; +const char *vl = "\xba"; +const char *hl = "\xcd"; +const char *td = "\xcb"; +const char *tu = "\xca"; +const char *lc = "\xb3"; +const char *lf = "\xdb"; +const char *pr = "\xfe"; +const char *clear_screen = "cls"; + +#endif +// ------------------------------ + +// ------------------------------ +// Linux Specific Code +// ------------------------------ +#ifdef __linux__ +#include +#include +#include +#include +#define semaphore sem_t + +typedef unsigned char bool; +#define true 1 +#define false 0 + +// Wait for a semaphore +void semaphore_wait(semaphore *s) { + // Sem wait + sem_wait(s); +} + +// Signal a semaphore +void semaphore_signal(semaphore *s) { + // Sem post + sem_post(s); +} + +// Create a semaphore initialised to value +void semaphore_create(semaphore *s, int value) { + // Initialise Semaphore + sem_init(s, 0, value); +} +#endif +// ------------------------------ + +// ------------------------------ +// macOS Specific Code +// ------------------------------ +#ifdef __APPLE__ + +#include +#include +#include +#define semaphore dispatch_semaphore_t + +// Wait for a semaphore +void semaphore_wait(semaphore *s) { + // Dispatch wait + dispatch_semaphore_wait(*s, DISPATCH_TIME_FOREVER); +} + +// Signal a semaphore +void semaphore_signal(semaphore *s) { + // Dispatch signal + dispatch_semaphore_signal(*s); +} + +// Create a semaphore initialised to value +void semaphore_create(semaphore *s, int value) { + // Dispatch create semaphore + (*s) = dispatch_semaphore_create(value); +} +#endif +// ------------------------------ + +// ------------------------------ +// UNIX Specific Code +// ------------------------------ +#if defined(__APPLE__) || defined(__linux__) + +// Create a thread (pthreads) +void create_thread(void *(*func)(void*), void *args) { + // Create Thread - POSIX API + pthread_t thread; + pthread_create(&thread, NULL, func, args); +} + +// Constants for printing +const char *tl = "\u250f"; +const char *tr = "\u2513"; +const char *bl = "\u2517"; +const char *br = "\u251b"; +const char *vl = "\u2503"; +const char *hl = "\u2501"; +const char *td = "\u2533"; +const char *tu = "\u253b"; +const char *lc = "\u2502"; +const char *lf = "\u2588"; +const char *pr = "\u2596"; +const char *clear_screen = "clear"; + +// Sleep (in milliseconds) +void Sleep(unsigned long long milliseconds) { + // Sleep for nanoseconds + struct timespec req = {0}; + req.tv_sec = milliseconds / 1000ULL; + req.tv_nsec = (milliseconds - (req.tv_sec*1000ULL)) * 1000000ULL; + nanosleep(&req, (struct timespec *)NULL); +} +#endif + +#endif // LIFT_H_ \ No newline at end of file