2015-09-07 11:44:36 -06:00
|
|
|
// enums/VendingMachine.java
|
2015-12-15 11:47:04 -08:00
|
|
|
// (c)2016 MindView LLC: see Copyright.txt
|
2015-11-15 15:51:35 -08:00
|
|
|
// We make no guarantees that this code is fit for any purpose.
|
|
|
|
// Visit http://mindviewinc.com/Books/OnJava/ for more book information.
|
2016-07-28 13:42:03 -06:00
|
|
|
// {java VendingMachine VendingMachineInput.txt}
|
2015-06-15 17:47:35 -07:00
|
|
|
import java.util.*;
|
2015-12-06 11:45:16 -08:00
|
|
|
import java.io.IOException;
|
2015-11-03 12:00:44 -08:00
|
|
|
import java.util.function.*;
|
2015-12-06 11:45:16 -08:00
|
|
|
import java.nio.file.*;
|
|
|
|
import java.util.stream.*;
|
2015-06-15 17:47:35 -07:00
|
|
|
|
|
|
|
enum Category {
|
|
|
|
MONEY(Input.NICKEL, Input.DIME,
|
|
|
|
Input.QUARTER, Input.DOLLAR),
|
|
|
|
ITEM_SELECTION(Input.TOOTHPASTE, Input.CHIPS,
|
|
|
|
Input.SODA, Input.SOAP),
|
|
|
|
QUIT_TRANSACTION(Input.ABORT_TRANSACTION),
|
|
|
|
SHUT_DOWN(Input.STOP);
|
|
|
|
private Input[] values;
|
|
|
|
Category(Input... types) { values = types; }
|
|
|
|
private static EnumMap<Input,Category> categories =
|
|
|
|
new EnumMap<>(Input.class);
|
|
|
|
static {
|
|
|
|
for(Category c : Category.class.getEnumConstants())
|
|
|
|
for(Input type : c.values)
|
|
|
|
categories.put(type, c);
|
|
|
|
}
|
|
|
|
public static Category categorize(Input input) {
|
|
|
|
return categories.get(input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class VendingMachine {
|
|
|
|
private static State state = State.RESTING;
|
|
|
|
private static int amount = 0;
|
|
|
|
private static Input selection = null;
|
|
|
|
enum StateDuration { TRANSIENT } // Tagging enum
|
|
|
|
enum State {
|
|
|
|
RESTING {
|
|
|
|
@Override
|
|
|
|
void next(Input input) {
|
|
|
|
switch(Category.categorize(input)) {
|
|
|
|
case MONEY:
|
|
|
|
amount += input.amount();
|
|
|
|
state = ADDING_MONEY;
|
|
|
|
break;
|
|
|
|
case SHUT_DOWN:
|
|
|
|
state = TERMINAL;
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ADDING_MONEY {
|
|
|
|
@Override
|
|
|
|
void next(Input input) {
|
|
|
|
switch(Category.categorize(input)) {
|
|
|
|
case MONEY:
|
|
|
|
amount += input.amount();
|
|
|
|
break;
|
|
|
|
case ITEM_SELECTION:
|
|
|
|
selection = input;
|
|
|
|
if(amount < selection.amount())
|
2015-11-03 12:00:44 -08:00
|
|
|
System.out.println(
|
|
|
|
"Insufficient money for " + selection);
|
2015-06-15 17:47:35 -07:00
|
|
|
else state = DISPENSING;
|
|
|
|
break;
|
|
|
|
case QUIT_TRANSACTION:
|
|
|
|
state = GIVING_CHANGE;
|
|
|
|
break;
|
|
|
|
case SHUT_DOWN:
|
|
|
|
state = TERMINAL;
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
DISPENSING(StateDuration.TRANSIENT) {
|
|
|
|
@Override
|
|
|
|
void next() {
|
2015-11-03 12:00:44 -08:00
|
|
|
System.out.println("here is your " + selection);
|
2015-06-15 17:47:35 -07:00
|
|
|
amount -= selection.amount();
|
|
|
|
state = GIVING_CHANGE;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
GIVING_CHANGE(StateDuration.TRANSIENT) {
|
|
|
|
@Override
|
|
|
|
void next() {
|
|
|
|
if(amount > 0) {
|
2015-11-03 12:00:44 -08:00
|
|
|
System.out.println("Your change: " + amount);
|
2015-06-15 17:47:35 -07:00
|
|
|
amount = 0;
|
|
|
|
}
|
|
|
|
state = RESTING;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
TERMINAL {@Override
|
2015-11-03 12:00:44 -08:00
|
|
|
void output() { System.out.println("Halted"); } };
|
2015-06-15 17:47:35 -07:00
|
|
|
private boolean isTransient = false;
|
|
|
|
State() {}
|
|
|
|
State(StateDuration trans) { isTransient = true; }
|
|
|
|
void next(Input input) {
|
|
|
|
throw new RuntimeException("Only call " +
|
|
|
|
"next(Input input) for non-transient states");
|
|
|
|
}
|
|
|
|
void next() {
|
|
|
|
throw new RuntimeException("Only call next() for " +
|
|
|
|
"StateDuration.TRANSIENT states");
|
|
|
|
}
|
2015-11-03 12:00:44 -08:00
|
|
|
void output() { System.out.println(amount); }
|
2015-06-15 17:47:35 -07:00
|
|
|
}
|
2015-11-03 12:00:44 -08:00
|
|
|
static void run(Supplier<Input> gen) {
|
2015-06-15 17:47:35 -07:00
|
|
|
while(state != State.TERMINAL) {
|
2015-11-03 12:00:44 -08:00
|
|
|
state.next(gen.get());
|
2015-06-15 17:47:35 -07:00
|
|
|
while(state.isTransient)
|
|
|
|
state.next();
|
|
|
|
state.output();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public static void main(String[] args) {
|
2015-11-03 12:00:44 -08:00
|
|
|
Supplier<Input> gen = new RandomInputSupplier();
|
2015-06-15 17:47:35 -07:00
|
|
|
if(args.length == 1)
|
2015-11-03 12:00:44 -08:00
|
|
|
gen = new FileInputSupplier(args[0]);
|
2015-06-15 17:47:35 -07:00
|
|
|
run(gen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// For a basic sanity check:
|
2015-11-03 12:00:44 -08:00
|
|
|
class RandomInputSupplier implements Supplier<Input> {
|
2015-06-15 17:47:35 -07:00
|
|
|
@Override
|
2015-11-03 12:00:44 -08:00
|
|
|
public Input get() { return Input.randomSelection(); }
|
2015-06-15 17:47:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create Inputs from a file of ';'-separated strings:
|
2015-11-03 12:00:44 -08:00
|
|
|
class FileInputSupplier implements Supplier<Input> {
|
2015-06-15 17:47:35 -07:00
|
|
|
private Iterator<String> input;
|
2015-11-03 12:00:44 -08:00
|
|
|
public FileInputSupplier(String fileName) {
|
2015-12-06 11:45:16 -08:00
|
|
|
try {
|
|
|
|
input = Files.lines(Paths.get(fileName))
|
|
|
|
.skip(1) // Skip the comment line
|
|
|
|
.flatMap(s -> Arrays.stream(s.split(";")))
|
|
|
|
.map(String::trim)
|
|
|
|
.collect(Collectors.toList())
|
|
|
|
.iterator();
|
|
|
|
} catch(IOException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
2015-06-15 17:47:35 -07:00
|
|
|
}
|
|
|
|
@Override
|
2015-11-03 12:00:44 -08:00
|
|
|
public Input get() {
|
2015-06-15 17:47:35 -07:00
|
|
|
if(!input.hasNext())
|
|
|
|
return null;
|
|
|
|
return Enum.valueOf(Input.class, input.next().trim());
|
|
|
|
}
|
2015-09-07 11:44:36 -06:00
|
|
|
}
|
|
|
|
/* Output:
|
2015-06-15 17:47:35 -07:00
|
|
|
25
|
|
|
|
50
|
2015-12-06 11:45:16 -08:00
|
|
|
75
|
|
|
|
here is your CHIPS
|
|
|
|
0
|
|
|
|
100
|
|
|
|
200
|
2015-06-15 17:47:35 -07:00
|
|
|
here is your TOOTHPASTE
|
|
|
|
0
|
|
|
|
25
|
|
|
|
35
|
|
|
|
Your change: 35
|
|
|
|
0
|
|
|
|
25
|
|
|
|
35
|
|
|
|
Insufficient money for SODA
|
|
|
|
35
|
|
|
|
60
|
|
|
|
70
|
|
|
|
75
|
|
|
|
Insufficient money for SODA
|
|
|
|
75
|
|
|
|
Your change: 75
|
|
|
|
0
|
|
|
|
Halted
|
2015-09-07 11:44:36 -06:00
|
|
|
*/
|