164 lines
3.9 KiB
Java
164 lines
3.9 KiB
Java
//: enumerated/VendingMachine.java
|
|
// {CompileTimeError} Not working in Java 8
|
|
// {Args: VendingMachineInput.txt}
|
|
import java.util.*;
|
|
import net.mindview.util.*;
|
|
import static enumerated.Input.*;
|
|
import static net.mindview.util.Print.*;
|
|
|
|
enum Category {
|
|
MONEY(NICKEL, DIME, QUARTER, DOLLAR),
|
|
ITEM_SELECTION(TOOTHPASTE, CHIPS, SODA, SOAP),
|
|
QUIT_TRANSACTION(ABORT_TRANSACTION),
|
|
SHUT_DOWN(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())
|
|
print("Insufficient money for " + selection);
|
|
else state = DISPENSING;
|
|
break;
|
|
case QUIT_TRANSACTION:
|
|
state = GIVING_CHANGE;
|
|
break;
|
|
case SHUT_DOWN:
|
|
state = TERMINAL;
|
|
default:
|
|
}
|
|
}
|
|
},
|
|
DISPENSING(StateDuration.TRANSIENT) {
|
|
@Override
|
|
void next() {
|
|
print("here is your " + selection);
|
|
amount -= selection.amount();
|
|
state = GIVING_CHANGE;
|
|
}
|
|
},
|
|
GIVING_CHANGE(StateDuration.TRANSIENT) {
|
|
@Override
|
|
void next() {
|
|
if(amount > 0) {
|
|
print("Your change: " + amount);
|
|
amount = 0;
|
|
}
|
|
state = RESTING;
|
|
}
|
|
},
|
|
TERMINAL {@Override
|
|
void output() { print("Halted"); } };
|
|
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");
|
|
}
|
|
void output() { print(amount); }
|
|
}
|
|
static void run(Generator<Input> gen) {
|
|
while(state != State.TERMINAL) {
|
|
state.next(gen.next());
|
|
while(state.isTransient)
|
|
state.next();
|
|
state.output();
|
|
}
|
|
}
|
|
public static void main(String[] args) {
|
|
Generator<Input> gen = new RandomInputGenerator();
|
|
if(args.length == 1)
|
|
gen = new FileInputGenerator(args[0]);
|
|
run(gen);
|
|
}
|
|
}
|
|
|
|
// For a basic sanity check:
|
|
class RandomInputGenerator implements Generator<Input> {
|
|
@Override
|
|
public Input next() { return Input.randomSelection(); }
|
|
}
|
|
|
|
// Create Inputs from a file of ';'-separated strings:
|
|
class FileInputGenerator implements Generator<Input> {
|
|
private Iterator<String> input;
|
|
public FileInputGenerator(String fileName) {
|
|
input = new TextFile(fileName, ";").iterator();
|
|
}
|
|
@Override
|
|
public Input next() {
|
|
if(!input.hasNext())
|
|
return null;
|
|
return Enum.valueOf(Input.class, input.next().trim());
|
|
}
|
|
} /* Output:
|
|
25
|
|
50
|
|
75
|
|
here is your CHIPS
|
|
0
|
|
100
|
|
200
|
|
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
|
|
*///:~
|