/* * The MIT License (MIT) * * Copyright (c) 2015 Camil Staps * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.camilstaps.route66; import java.util.ArrayList; import java.util.Observable; import javax.swing.JFrame; /** * Route 66 model with crossing * * The class model holds all cars in the simulation. * * This implementation of the crossing is different from the idea that is * vaguely described in the assignment. I tried to implement that idea, but it * was too vague and in the end a student assistent told me to put everything * in the Model. * * @author Pieter Koopman, Camil Staps */ public class Model extends Observable { private final Car [] cars; public static final int DIRECTIONS = 4, NUMBEROFCARS = 5 * DIRECTIONS; // total number of cars in system private final ArrayList views; /** * Crossing attributes: * * allowed - which direction is currently allowed; if East is allowed, West * is also allowed, etc. * crossingWaiting - whether or not the crossing is waiting for cars to * leave the crossing to switch (i.e. orange) * lastCrossingChange - when the crossing switched the last time * MIN_CROSS_TIME - the minimum time the crossing should wait before * switching again after the last switch */ private Direction allowed = Direction.East; private boolean crossingWaiting = false; private long lastCrossingChange = 0; private static final int MIN_CROSS_TIME = 1000; /** * Constructor: create all cars */ public Model() { views = new ArrayList<>(); cars = new Car [NUMBEROFCARS]; for (int c = 0; c < NUMBEROFCARS; c += 1) { cars[c] = new Car(c, this); } } /** * add the view to this model. It will be repainted upon an update * @param view */ public void addView(JFrame view) { views.add(view); } public Car[] getCars() { return cars; } /** * Get a car from the model * @param i numbers of required car * @return the car itself (not a copy) */ public Car getCar(int i) { return cars[i]; } /** * Repaint all views, and notify all drivers to reconsider driving */ public synchronized void update() { for (JFrame view: views) { view.repaint(); } notifyAll(); } /** * Check if a location is safe for a car to go to. This should always be * checked by a driver before actually driving. * @param car * @param requested_location * @return */ public synchronized boolean isSafeLocation(Car car, int requested_location) { // Check that we don't collide with the car in front of us Car that_car = cars[car.getNumber() < DIRECTIONS ? car.getNumber() + NUMBEROFCARS - DIRECTIONS : car.getNumber() - DIRECTIONS]; boolean ok = !(that_car.getLocation() > requested_location && that_car.getLocation() < requested_location + Car.CARLENGTH + Car.MINCARSPACE); // If we have to wait for the crossing... well, do that. if (car.isInFrontOfCrossing() && !isCrossingAllowed(car.getDirection())) { ok = doCrossingRequest(); } if (!ok) { try { wait(); } catch (InterruptedException ex) {} } return ok; } /** * Check if there are any cars on the crossing * @return */ public synchronized boolean isCarsOnCrossing() { for (Car car : cars) { if (car.isOnCrossing()) { return true; } } return false; } /** * Switch the crossing */ public synchronized void doSwitchCrossing() { crossingWaiting = true; while (isCarsOnCrossing()) { try { wait(); } catch (InterruptedException ex) { } } if (allowed == Direction.East || allowed == Direction.West) { allowed = Direction.North; } else { allowed = Direction.East; } crossingWaiting = false; lastCrossingChange = System.currentTimeMillis(); } /** * Check if crossing in some direction is allowed * @param direction * @return */ public synchronized boolean isCrossingAllowed(Direction direction) { return !crossingWaiting && (direction == allowed || Direction.opposite(direction) == allowed); } /** * Try to switch the crossing * This may return false without even trying to switch, if it's too early to * ask (see {@link Model#MIN_CROSS_TIME}) or if a request has been made already. * @return */ public synchronized boolean doCrossingRequest() { if (crossingWaiting || System.currentTimeMillis() - lastCrossingChange < MIN_CROSS_TIME) { return false; } doSwitchCrossing(); return true; } }