commit 08f94aa5d3e00b1aae2f79cb8523dc876d028320 Author: Yadunand Prem Date: Thu Aug 25 17:25:02 2022 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e7974b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +Lab1.pdf \ No newline at end of file diff --git a/ArrivalEvent.java b/ArrivalEvent.java new file mode 100644 index 0000000..54816a5 --- /dev/null +++ b/ArrivalEvent.java @@ -0,0 +1,34 @@ + +class ArrivalEvent extends BaseShopEvent { + + double serviceTime; + + public ArrivalEvent(double time, int customerId, boolean[] available, double serviceTime) { + super(time, customerId, available); + this.availableCounters = available; + this.serviceTime = serviceTime; + } + + @Override + public Event[] simulate() { + int counter = -1; + for (int i = 0; i < this.availableCounters.length; i++) { + if (this.availableCounters[i]) { + counter = i; + break; + } + } + if (counter == -1) { + return new Event[] { new DepartureEvent(this.getTime(), customerId, availableCounters) }; + } + + return new Event[] { + new ServiceBeginEvent(this.getTime(), this.customerId, this.availableCounters, this.serviceTime, + counter) }; + } + + @Override + public String toString() { + return super.toString() + String.format(": Customer %d arrives", this.customerId); + } +} \ No newline at end of file diff --git a/BaseShopEvent.java b/BaseShopEvent.java new file mode 100644 index 0000000..13760ac --- /dev/null +++ b/BaseShopEvent.java @@ -0,0 +1,22 @@ + +/** + * This class encapsulates an event in the shop + * simulation. Your task is to replace this + * class with new classes, following proper OOP principles. + * + * @author Wei Tsang + * @version CS2030S AY21/22 Semester 2 + */ + +abstract class BaseShopEvent extends Event { + + final int customerId; + boolean[] availableCounters; + + public BaseShopEvent(double time, int customerId, boolean[] availableCounters) { + super(time); + this.customerId = customerId; + this.availableCounters = availableCounters; + } + +} \ No newline at end of file diff --git a/DepartureEvent.java b/DepartureEvent.java new file mode 100644 index 0000000..ce376a8 --- /dev/null +++ b/DepartureEvent.java @@ -0,0 +1,19 @@ + +class DepartureEvent extends BaseShopEvent { + + public DepartureEvent(double time, int customerId, boolean[] available) { + super(time, customerId, available); + } + + @Override + public String toString() { + return super.toString() + + String.format(": Customer %d departed", this.customerId); + } + + @Override + public Event[] simulate() { + return new Event[] {}; + } + +} \ No newline at end of file diff --git a/Event.java b/Event.java new file mode 100644 index 0000000..8ae0b75 --- /dev/null +++ b/Event.java @@ -0,0 +1,72 @@ +/** + * The Event class is an abstract class that encapsulates a + * discrete event to be simulated. An event encapsulates the + * time the event occurs. A subclass of event _must_ override + * the simulate() method to implement the logic of the + * simulation when this event is simulated. The simulate method + * returns an array of events, which the simulator will then + * add to the event queue. Note that an event also implements + * the Comparable interface so that a PriorityQueue can + * arrange the events in the order of event time. + * + * @author Wei Tsang + * @version CS2030S AY21/22 Semester 2 + */ +abstract class Event implements Comparable { + /** The time this event occurs in the simulation. */ + private final double time; + + /** + * Creates an event that occurs at the given time. + * + * @param time The time the event occurs. + */ + public Event(double time) { + this.time = time; + } + + /** + * Getter to return the time of this event. + * + * @return The time this event occurs. + */ + public double getTime() { + return this.time; + } + + /** + * Compare this event with a given event e. + * + * @param e The other event to compare to. + * @return 1 if this event occurs later than e; + * 0 if they occur the same time; + * -1 if this event occurs earlier. + */ + @Override + public int compareTo(Event e) { + if (this.time > e.time) { + return 1; + } else if (this.time == e.time) { + return 0; + } else { + return -1; + } + } + + /** + * Return the string representation this event. + * + * @return A string consists of the time this event occurs. + */ + @Override + public String toString() { + return String.format("%.3f", this.time); + } + + /** + * Simulate this event. + * + * @return An array of new events to be scheduled by the simulator. + */ + public abstract Event[] simulate(); +} diff --git a/Lab1.java b/Lab1.java new file mode 100644 index 0000000..e87a597 --- /dev/null +++ b/Lab1.java @@ -0,0 +1,26 @@ +import java.util.Scanner; + +/** + * The main class for CS2030S Lab 1. + * + * @author Wei Tsang + * @version CS2030S AY21/22 Semester 2 + */ +class Lab1 { + public static void main(String[] args) { + + // Create a scanner to read from standard input. + Scanner sc = new Scanner(System.in); + + // Create a simulation. The ShopSimulation + // constructor will read the simulation parameters + // and initial events using the scanner. + Simulation simulation = new ShopSimulation(sc); + + // Create a new simulator and run the simulation + new Simulator(simulation).run(); + + // Clean up the scanner. + sc.close(); + } +} diff --git a/ServiceBeginEvent.java b/ServiceBeginEvent.java new file mode 100644 index 0000000..372f410 --- /dev/null +++ b/ServiceBeginEvent.java @@ -0,0 +1,27 @@ + +class ServiceBeginEvent extends BaseShopEvent { + + double serviceTime; + int counterId; + + public ServiceBeginEvent(double time, int customerId, boolean[] available, double serviceTime, int counterId) { + super(time, customerId, available); + this.serviceTime = serviceTime; + this.counterId = counterId; + // TODO Auto-generated constructor stub + } + + @Override + public String toString() { + return super.toString() + + String.format(": Customer %d service begin (by Counter %d)", this.customerId, this.counterId); + } + + @Override + public Event[] simulate() { + this.availableCounters[this.counterId] = false; + double endTime = this.getTime() + this.serviceTime; + return new Event[] { + new ServiceEndEvent(endTime, this.customerId, this.availableCounters, this.counterId) }; + } +} \ No newline at end of file diff --git a/ServiceEndEvent.java b/ServiceEndEvent.java new file mode 100644 index 0000000..1d9567d --- /dev/null +++ b/ServiceEndEvent.java @@ -0,0 +1,23 @@ + +class ServiceEndEvent extends BaseShopEvent { + + int counterId; + + public ServiceEndEvent(double time, int customerId, boolean[] available, int counterId) { + super(time, customerId, available); + this.counterId = counterId; + // TODO Auto-generated constructor stub + } + + @Override + public String toString() { + return super.toString() + + String.format(": Customer %d service done (by Counter %d)", this.customerId, this.counterId); + } + + @Override + public Event[] simulate() { + this.availableCounters[counterId] = true; + return new Event[] { new DepartureEvent(this.getTime(), customerId, availableCounters) }; + } +} \ No newline at end of file diff --git a/ShopSimulation.java b/ShopSimulation.java new file mode 100644 index 0000000..814e837 --- /dev/null +++ b/ShopSimulation.java @@ -0,0 +1,58 @@ +import java.util.Scanner; + +/** + * This class implements a shop simulation. + * + * @author Wei Tsang + * @version CS2030S AY21/22 Semester 2 + */ +class ShopSimulation extends Simulation { + /** + * The availability of counters in the shop. + */ + public boolean[] availableCounters; + + /** + * The list of customer arrival events to populate + * the simulation with. + */ + public Event[] initEvents; + + /** + * Constructor for a shop simulation. + * + * @param sc A scanner to read the parameters from. The first + * integer scanned is the number of customers; followed + * by the number of service counters. Next is a + * sequence of (arrival time, service time) pair, each + * pair represents a customer. + */ + public ShopSimulation(Scanner sc) { + initEvents = new Event[sc.nextInt()]; + int numOfCounters = sc.nextInt(); + + availableCounters = new boolean[numOfCounters]; + for (int i = 0; i < numOfCounters; i++) { + availableCounters[i] = true; + } + + int id = 0; + while (sc.hasNextDouble()) { + double arrivalTime = sc.nextDouble(); + double serviceTime = sc.nextDouble(); + initEvents[id] = new ArrivalEvent(arrivalTime, id, availableCounters, serviceTime); + id += 1; + } + } + + /** + * Retrieve an array of events to populate the + * simulator with. + * + * @return An array of events for the simulator. + */ + @Override + public Event[] getInitialEvents() { + return initEvents; + } +} diff --git a/Simulation.java b/Simulation.java new file mode 100644 index 0000000..8b6a465 --- /dev/null +++ b/Simulation.java @@ -0,0 +1,20 @@ +/** + * This class is a general abstract class that + * encapsulates a simulation. To implement a + * simulation, inherit from this class and implement + * the `getInitialEvents` method. + * + * @author Wei Tsang + * @version CS2030S AY21/22 Semester 2 + */ +abstract class Simulation { + /** + * An abstract method to return an array of events + * used to initialize the simulation. + * + * @return An array of initial events that the + * simulator can use to kick-start the + * simulation. + */ + public abstract Event[] getInitialEvents(); +} diff --git a/Simulator.java b/Simulator.java new file mode 100644 index 0000000..0166db7 --- /dev/null +++ b/Simulator.java @@ -0,0 +1,51 @@ +import java.util.PriorityQueue; + +/** + * This class implements a discrete event simulator. + * The simulator maintains a priority queue of events. + * It runs through the events and simulates each one until + * the queue is empty. + * + * @author Wei Tsang + * @version CS2030S AY21/22 Semester 2 + */ +public class Simulator { + /** The event queue. */ + private final PriorityQueue events; + + /** + * The constructor for a simulator. It takes in + * a simulation as an argument, and calls the + * getInitialEvents method of that simulation to + * initialize the event queue. + * + * @param simulation The simulation to simulate. + */ + public Simulator(Simulation simulation) { + this.events = new PriorityQueue(); + for (Event e : simulation.getInitialEvents()) { + this.events.add(e); + } + } + + /** + * Run the simulation until no more events is in + * the queue. For each event in the queue (in + * increasing order of time), print out its string + * representation, then simulate it. If the + * simulation returns one or more events, add them + * to the queue, and repeat. + */ + public void run() { + Event event = this.events.poll(); + while (event != null) { + System.out.println(event); + Event[] newEvents = event.simulate(); + for (Event e : newEvents) { + this.events.add(e); + } + event = this.events.poll(); + } + return; + } +} diff --git a/inputs/Lab1.1.in b/inputs/Lab1.1.in new file mode 100644 index 0000000..6fde254 --- /dev/null +++ b/inputs/Lab1.1.in @@ -0,0 +1,4 @@ +3 1 +1.0 1.0 +3.0 1.0 +5.0 1.0 diff --git a/inputs/Lab1.2.in b/inputs/Lab1.2.in new file mode 100644 index 0000000..efc9f6e --- /dev/null +++ b/inputs/Lab1.2.in @@ -0,0 +1,4 @@ +3 1 +1.1 2.0 +2.2 2.0 +3.3 2.0 diff --git a/inputs/Lab1.3.in b/inputs/Lab1.3.in new file mode 100644 index 0000000..d63c1c7 --- /dev/null +++ b/inputs/Lab1.3.in @@ -0,0 +1,6 @@ +5 2 +1.0 1.0 +1.2 1.0 +1.4 1.0 +1.6 1.0 +2.1 1.0 diff --git a/inputs/Lab1.4.in b/inputs/Lab1.4.in new file mode 100644 index 0000000..232e851 --- /dev/null +++ b/inputs/Lab1.4.in @@ -0,0 +1,5 @@ +4 2 +1.0 1.0 +1.1 1.0 +2.2 1.0 +2.3 1.0 diff --git a/inputs/Lab1.5.in b/inputs/Lab1.5.in new file mode 100644 index 0000000..1bf5387 --- /dev/null +++ b/inputs/Lab1.5.in @@ -0,0 +1,4 @@ +3 2 +1.0 4 +2.1 1 +4.2 1 diff --git a/outputs/Lab1.1.out b/outputs/Lab1.1.out new file mode 100644 index 0000000..468dc2f --- /dev/null +++ b/outputs/Lab1.1.out @@ -0,0 +1,12 @@ +1.000: Customer 0 arrives +1.000: Customer 0 service begin (by Counter 0) +2.000: Customer 0 service done (by Counter 0) +2.000: Customer 0 departed +3.000: Customer 1 arrives +3.000: Customer 1 service begin (by Counter 0) +4.000: Customer 1 service done (by Counter 0) +4.000: Customer 1 departed +5.000: Customer 2 arrives +5.000: Customer 2 service begin (by Counter 0) +6.000: Customer 2 service done (by Counter 0) +6.000: Customer 2 departed diff --git a/outputs/Lab1.2.out b/outputs/Lab1.2.out new file mode 100644 index 0000000..901e4f2 --- /dev/null +++ b/outputs/Lab1.2.out @@ -0,0 +1,10 @@ +1.100: Customer 0 arrives +1.100: Customer 0 service begin (by Counter 0) +2.200: Customer 1 arrives +2.200: Customer 1 departed +3.100: Customer 0 service done (by Counter 0) +3.100: Customer 0 departed +3.300: Customer 2 arrives +3.300: Customer 2 service begin (by Counter 0) +5.300: Customer 2 service done (by Counter 0) +5.300: Customer 2 departed diff --git a/outputs/Lab1.3.out b/outputs/Lab1.3.out new file mode 100644 index 0000000..ea09baa --- /dev/null +++ b/outputs/Lab1.3.out @@ -0,0 +1,16 @@ +1.000: Customer 0 arrives +1.000: Customer 0 service begin (by Counter 0) +1.200: Customer 1 arrives +1.200: Customer 1 service begin (by Counter 1) +1.400: Customer 2 arrives +1.400: Customer 2 departed +1.600: Customer 3 arrives +1.600: Customer 3 departed +2.000: Customer 0 service done (by Counter 0) +2.000: Customer 0 departed +2.100: Customer 4 arrives +2.100: Customer 4 service begin (by Counter 0) +2.200: Customer 1 service done (by Counter 1) +2.200: Customer 1 departed +3.100: Customer 4 service done (by Counter 0) +3.100: Customer 4 departed diff --git a/outputs/Lab1.4.out b/outputs/Lab1.4.out new file mode 100644 index 0000000..65c50af --- /dev/null +++ b/outputs/Lab1.4.out @@ -0,0 +1,16 @@ +1.000: Customer 0 arrives +1.000: Customer 0 service begin (by Counter 0) +1.100: Customer 1 arrives +1.100: Customer 1 service begin (by Counter 1) +2.000: Customer 0 service done (by Counter 0) +2.000: Customer 0 departed +2.100: Customer 1 service done (by Counter 1) +2.100: Customer 1 departed +2.200: Customer 2 arrives +2.200: Customer 2 service begin (by Counter 0) +2.300: Customer 3 arrives +2.300: Customer 3 service begin (by Counter 1) +3.200: Customer 2 service done (by Counter 0) +3.200: Customer 2 departed +3.300: Customer 3 service done (by Counter 1) +3.300: Customer 3 departed diff --git a/outputs/Lab1.5.out b/outputs/Lab1.5.out new file mode 100644 index 0000000..771a25b --- /dev/null +++ b/outputs/Lab1.5.out @@ -0,0 +1,12 @@ +1.000: Customer 0 arrives +1.000: Customer 0 service begin (by Counter 0) +2.100: Customer 1 arrives +2.100: Customer 1 service begin (by Counter 1) +3.100: Customer 1 service done (by Counter 1) +3.100: Customer 1 departed +4.200: Customer 2 arrives +4.200: Customer 2 service begin (by Counter 1) +5.000: Customer 0 service done (by Counter 0) +5.000: Customer 0 departed +5.200: Customer 2 service done (by Counter 1) +5.200: Customer 2 departed diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..aab80a4 --- /dev/null +++ b/test.sh @@ -0,0 +1,73 @@ +#!/bin/bash +function control_c() { + if [ -e $out ] + then + rm -f $out + fi +} + +trap control_c INT + +if [ $# -ne 1 ] +then + echo "usage: $0
" + exit 1 +fi + +PROG=$1 +if [ ! -e $PROG.class ] +then + echo "$PROG.class does not exist. Have you compiled it with make or javac?" + exit 1 +fi + +num_failed=0 +i=1 +while true +do + if [ -e inputs/$PROG.$i.in ] + then + if [ $(uname) == "Darwin" ] + then + out=$(mktemp -t $PROG) + else + out=$(mktemp --suffix=$PROG) + fi + java $PROG < inputs/$PROG.$i.in > $out + status="$?" + if [ "$status" -ne "0" ] + then + echo "$PROG: return non-zero status $status for test case $i" + # cat inputs/$PROG.$i.in + num_failed=$((num_failed + 1)) + else + if [ -e $out ] + then + if [ `diff -bB $out outputs/$PROG.$i.out | wc -l` -ne 0 ] + then + echo "test $i: failed" + #cat inputs/$PROG.$i.in + num_failed=$((num_failed + 1)) + else + echo "test $i: passed" + fi + rm -f $out + else + echo "$PROG: cannot find output file. Execution interrupted?" + num_failed=$((num_failed + 1)) + fi + fi + i=$((i + 1)) + else + break + fi +done + +if [ $i -eq 1 ] +then + echo "$PROG: no test cases found 🤷" +elif [ $num_failed -eq 0 ] +then + echo "$PROG: passed everything 🎉" +fi +# vim:noexpandtab:sw=4:ts=4