Last active
June 26, 2021 09:20
-
-
Save jrg94/bd3574d9ce15e78d91906e3065643587 to your computer and use it in GitHub Desktop.
Bicycle Simulator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A bicycle consists of a chain assembly, a handle assembly, and a frame. | |
* This class can be used to simulate a bicycle. | |
*/ | |
public class Bicycle { | |
private BrakeAssembly brakeAssembly; | |
private ChainAssembly chainAssembly; | |
private HandleAssembly handleAssembly; | |
/** | |
* The default constructor for creating a bike. | |
*/ | |
public Bicycle(BrakeAssembly brakeAssembly, ChainAssembly chainAssembly, HandleAssembly handleAssembly) { | |
this.handleAssembly = handleAssembly; | |
this.chainAssembly = chainAssembly; | |
this.brakeAssembly = brakeAssembly; | |
} | |
/** | |
* Changes the pedal rate of the bike by some number of cycles. | |
* | |
* @param numCycles the number of cycles to accelerate the bike by | |
*/ | |
public void pedal(double numCycles) { | |
chainAssembly.accelerate(numCycles); | |
} | |
/** | |
* Changes the brake rate of the bike by some number of cycles. | |
* | |
* @param numCycles the number of cycles to brake the bike by | |
*/ | |
public void brake(double numCycles) { | |
brakeAssembly.brake(numCycles); | |
} | |
/** | |
* Changes the angle of the front axle the bike. | |
* | |
* @param the angle to change the front axle by | |
*/ | |
public void turn(double angle) { | |
handleAssembly.turn(angle); | |
} | |
public double distance() { | |
return chainAssembly.distance(chainAssembly.getPedalRate() - brakeAssembly.getBrakeRate()); | |
} | |
/** | |
* Reports the state of the bike system as a string. | |
*/ | |
public String state() { | |
String pedalRate = String.format("The current pedal rate is: %f.", chainAssembly.getPedalRate()); | |
String brakeRate = String.format("The current brake rate is: %f.", brakeAssembly.getBrakeRate()); | |
String angle = String.format("The current angle is: %f.", handleAssembly.getAngle()); | |
String distance = String.format("During this event, the bike traveled %f meters.", this.distance()); | |
return String.format("%s\n%s\n%s\n%s\n", pedalRate, brakeRate, angle, distance); | |
} | |
/** | |
* Copies the bike to be used as a snapshot of its state. | |
*/ | |
public Bicycle copy() { | |
Bicycle copy = new Bicycle(this.brakeAssembly.copy(), this.chainAssembly.copy(), this.handleAssembly.copy()); | |
return copy; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BrakeAssembly { | |
private double brakeRate; // cycles/event | |
private static final double MINIMUM_BRAKING = 0; | |
/** | |
* The defaul constructor | |
*/ | |
public BrakeAssembly() { | |
this.brakeRate = 0; | |
} | |
/** | |
* Increments the brake rate by some constant value. | |
* | |
* @param brakeRate the rate of braking in number of cycles | |
*/ | |
public void brake(double brakeRate) { | |
if (this.brakeRate + brakeRate < MINIMUM_BRAKING) { | |
this.brakeRate = MINIMUM_BRAKING; | |
} else { | |
this.brakeRate += brakeRate; | |
} | |
} | |
/** | |
* Returns the brake rate. | |
*/ | |
public double getBrakeRate() { | |
return this.brakeRate; | |
} | |
/** | |
* Copies the brake assembly. | |
*/ | |
public BrakeAssembly copy() { | |
BrakeAssembly assembly = new BrakeAssembly(); | |
assembly.brake(this.getBrakeRate()); | |
return assembly; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A ChainAssembly consists of a pedal, two gears, a tire, and a chain. | |
* This class can be used to drive the front gear via the pedal method which | |
* drives the chain that drives the rear gear. The output of the pedal | |
* method can be used to drive a TireAssembly. | |
*/ | |
public class ChainAssembly { | |
private Gear frontGear; | |
private Gear backGear; | |
private Tire tire; | |
private double pedalRate; // cycles/event | |
private static final double MINIMUM_PEDALING = 0; | |
private static final double FULL_CYCLE = 360; // degrees | |
/** | |
* The default constructor. | |
*/ | |
public ChainAssembly(Gear frontGear, Gear backGear, Tire tire) { | |
this.frontGear = frontGear; | |
this.backGear = backGear; | |
this.tire = tire; | |
this.pedalRate = 0; | |
} | |
/** | |
* Drives the main gear by a number of cycles. | |
* | |
* @param cycles the number of rotations of the main gear | |
* @return the number of degrees the back gear was rotated by | |
*/ | |
private double driveMainGear(double cycles) { | |
double teeth = this.frontGear.rotateByAngle(cycles * FULL_CYCLE); | |
double degrees = this.backGear.rotateByTeeth(teeth); | |
return degrees; | |
} | |
/** | |
* Accelerates the pedal by a number of cycles. | |
* | |
* @param cycles the number of rotations of the pedal | |
*/ | |
public void accelerate(double cycles) { | |
if (this.pedalRate + cycles < MINIMUM_PEDALING) { | |
this.pedalRate = MINIMUM_PEDALING; | |
} else { | |
this.pedalRate += cycles; | |
} | |
} | |
/** | |
* Computes the distance covered by the tire given some number | |
* of pedal cycles. This function doesn't use pedal rate because | |
* distance is a function of pedal rate AND brake rate. | |
* | |
* @param cycles the number of cycles of the pedal for this event | |
* @return the distance covered by the tire | |
*/ | |
public double distance(double cycles) { | |
if (cycles <= 0) { | |
return 0; | |
} else { | |
double degrees = this.driveMainGear(cycles); | |
double distance = tire.rotate(degrees); | |
return distance; | |
} | |
} | |
/** | |
* Returns the pedal rate. | |
*/ | |
public double getPedalRate() { | |
return this.pedalRate; | |
} | |
/** | |
* Copies this chain assembly. | |
*/ | |
public ChainAssembly copy() { | |
ChainAssembly assembly = new ChainAssembly(this.frontGear.copy(), this.backGear.copy(), this.tire.copy()); | |
assembly.accelerate(this.getPedalRate()); | |
return assembly; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A gear is a circular object that consists of | |
* evenly spaced teeth. All teeth are identical, | |
* so we don't have to worry about modeling them. | |
*/ | |
public class Gear { | |
private int teethCount; | |
private static final double FULL_CYCLE = 360; // degrees | |
/** | |
* The default constructor | |
*/ | |
public Gear(int teethCount) { | |
this.teethCount = teethCount; | |
} | |
/** | |
* Rotates the gear by some angle. | |
* | |
* @param degrees some angle | |
* @return the number of teeth rotated | |
*/ | |
public double rotateByAngle(double degrees) { | |
return (degrees / FULL_CYCLE) * this.teethCount; | |
} | |
/** | |
* Rotates the gear by some number of teeth. | |
* | |
* @param teeth some number of teeth | |
* @return the degrees the gear was rotated by | |
*/ | |
public double rotateByTeeth(double teeth) { | |
return (teeth / this.teethCount) * FULL_CYCLE; | |
} | |
/** | |
* Copies this gear. | |
*/ | |
public Gear copy() { | |
Gear copy = new Gear(this.teethCount); | |
return copy; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A Handle Assembly consists of a tire and a set of handles. | |
*/ | |
public class HandleAssembly { | |
private Tire tire; | |
private double angle; | |
private static final double MAX_ANGLE = 45; // degrees | |
/** | |
* Default constructor | |
*/ | |
public HandleAssembly(Tire tire) { | |
this.tire = tire; | |
} | |
/** | |
* Turns the handle system by some degrees. There are bounds | |
* on both ends specified by the MAX_ANGLE constant. | |
* | |
* @param degrees some angle | |
*/ | |
public void turn(double degrees) { | |
if (this.angle + degrees > MAX_ANGLE) { | |
this.angle = MAX_ANGLE; | |
} else if (this.angle + degrees < -MAX_ANGLE) { | |
this.angle = -MAX_ANGLE; | |
} else { | |
this.angle += degrees; | |
} | |
} | |
/** | |
* Returns the angle of the handle assembly. | |
*/ | |
public double getAngle() { | |
return this.angle; | |
} | |
/** | |
* Copies this handle assembly | |
*/ | |
public HandleAssembly copy() { | |
HandleAssembly copy = new HandleAssembly(this.tire.copy()); | |
copy.turn(this.getAngle()); | |
return copy; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Scanner; | |
import java.util.ArrayList; | |
/** | |
* The simulator is a utility class that offers simulation | |
* funtions for bikes. | |
*/ | |
public class Simulator { | |
/** | |
* Processes a command on the bike. | |
*/ | |
public static void processCommand(String command, Bicycle bike) { | |
switch (command.toLowerCase()) { | |
case "brake": bike.brake(1.0); break; | |
case "release": bike.brake(-1.0); break; | |
case "pedal": bike.pedal(0.5); break; | |
case "coast": bike.pedal(-0.5); break; | |
case "left": bike.turn(-5); break; | |
case "right": bike.turn(5); break; | |
default: // Do nothing | |
} | |
} | |
/** | |
* Issues the directions for the simulator. | |
*/ | |
private static void simulatorDirections() { | |
System.out.println("-----------------------------------------------"); | |
System.out.println("Simulation has begun!"); | |
System.out.println("This simulator is event driven."); | |
System.out.println("At each iteration, you'll have the choice to [pedal/coast], turn [left/right], [brake/release], or do nothing."); | |
System.out.println("Selecting [pedal/coast] will change the rate of pedaling by +/-.5 cycles/event."); | |
System.out.println("Selecting [brake/release] will icrement the rate of braking by +/-1.0 cycles/event."); | |
System.out.println("Selecting [left/right] will change the angle of the front wheel by +/-5 degrees."); | |
System.out.println("When you're finished, use the [exit] command"); | |
System.out.println("-----------------------------------------------"); | |
} | |
/** | |
* A fun method which can be used to analyze the trajectory of | |
* the bike over all the events. | |
*/ | |
public static void analyzeEvents(ArrayList<Bicycle> events) { | |
double distance = 0; | |
for (Bicycle event : events) { | |
distance += event.distance(); | |
} | |
System.out.printf("In total, the bike traveled %f meters.\n", distance); | |
} | |
/** | |
* Runs the actual simulator given the bike. | |
*/ | |
public static void runSimulator(Bicycle bike, Scanner in) { | |
simulatorDirections(); | |
ArrayList<Bicycle> events = new ArrayList<Bicycle>(); | |
events.add(bike.copy()); | |
System.out.print("Issue a command:"); | |
String command = in.nextLine(); | |
while (!command.equalsIgnoreCase("exit")) { | |
processCommand(command, bike); | |
System.out.println(bike.state()); | |
events.add(bike.copy()); | |
System.out.print("Issue a command:"); | |
command = in.nextLine(); | |
} | |
analyzeEvents(events); | |
} | |
/** | |
* Runs the prompts to generate a bike | |
* | |
* @param in the Scanner to be used for prompting | |
* @return the bike | |
*/ | |
public static Bicycle buildBike(Scanner in) { | |
System.out.println("Welcome to the bike simulator! Let's build a bike."); | |
System.out.print("Please choose to [run] the default bike or [build] your own:"); | |
String choice = in.nextLine(); | |
while (choice.isEmpty() && !choice.equalsIgnoreCase("run") && !choice.equalsIgnoreCase("build")) { | |
System.out.println("Sorry, your choice was not valid."); | |
System.out.print("Did you want to [run] the default bike or [build] your own?"); | |
choice = in.nextLine(); | |
} | |
if (choice.equalsIgnoreCase("run")) { | |
return buildDefaultBike(); | |
} else { | |
return buildCustomBike(in); | |
} | |
} | |
/** | |
* Builds a custom bike (no error handling). | |
* | |
* @param in the scanner to request user input | |
* @return the user's custom bike | |
*/ | |
public static Bicycle buildCustomBike(Scanner in) { | |
System.out.print("Choose a number of teeth for the main gear:"); | |
Gear main = new Gear(in.nextInt()); | |
System.out.print("Choose a number of teeth for the rear gear:"); | |
Gear rear = new Gear(in.nextInt()); | |
System.out.print("Choose a radius for your tires:"); | |
double radius = in.nextDouble(); | |
Tire front = new Tire(radius); | |
Tire back = new Tire(radius); | |
ChainAssembly chainAssembly = new ChainAssembly(main, rear, back); | |
HandleAssembly handleAssembly = new HandleAssembly(front); | |
BrakeAssembly brakes = new BrakeAssembly(); | |
return new Bicycle(brakes, chainAssembly, handleAssembly); | |
} | |
/** | |
* Builds a default bicycle that has a 2.75 gear ratio | |
* and .3 meter radius tires. | |
* | |
* @return a default bike | |
*/ | |
public static Bicycle buildDefaultBike() { | |
Gear main = new Gear(44); | |
Gear rear = new Gear(16); | |
Tire front = new Tire(.3); | |
Tire back = new Tire(.3); | |
ChainAssembly chainAssembly = new ChainAssembly(main, rear, back); | |
HandleAssembly handleAssembly = new HandleAssembly(front); | |
BrakeAssembly brakes = new BrakeAssembly(); | |
return new Bicycle(brakes, chainAssembly, handleAssembly); | |
} | |
/** | |
* The main method which launches the simulator. | |
*/ | |
public static void main(String[] args) { | |
Scanner in = new Scanner(System.in); | |
Bicycle bike = buildBike(in); | |
runSimulator(bike, in); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* The Tire class represents a single tire system. | |
*/ | |
public class Tire { | |
private double radius; // meters | |
private static final double FULL_CYCLE = 360; // degrees | |
/** | |
* The default constructor | |
*/ | |
public Tire(double radius) { | |
this.radius = radius; | |
} | |
/** | |
* Rotates the tire by some angle. | |
* | |
* @param degrees some angle | |
* @return the linear distance traveled by the tire | |
*/ | |
public double rotate(double degrees) { | |
double circumference = 2 * Math.PI * this.radius; | |
double distance = circumference * (degrees / FULL_CYCLE); | |
return distance; | |
} | |
/** | |
* Copies this tire. | |
*/ | |
public Tire copy() { | |
Tire copy = new Tire(this.radius); | |
return copy; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Potential features: