RosettaCodeData/Task/24-game-Solve/Ceylon/24-game-solve.ceylon
2019-09-12 10:33:56 -07:00

133 lines
4.2 KiB
Ceylon

import ceylon.random {
DefaultRandom
}
shared sealed class Rational(numerator, denominator = 1) satisfies Numeric<Rational> {
shared Integer numerator;
shared Integer denominator;
Integer gcd(Integer a, Integer b) => if (b == 0) then a else gcd(b, a % b);
shared Rational inverted => Rational(denominator, numerator);
shared Rational simplified =>
let (largestFactor = gcd(numerator, denominator))
Rational(numerator / largestFactor, denominator / largestFactor);
divided(Rational other) => (this * other.inverted).simplified;
negated => Rational(-numerator, denominator).simplified;
plus(Rational other) =>
let (top = numerator*other.denominator + other.numerator*denominator,
bottom = denominator * other.denominator)
Rational(top, bottom).simplified;
times(Rational other) =>
Rational(numerator * other.numerator, denominator * other.denominator).simplified;
shared Integer integer => numerator / denominator;
shared Float float => numerator.float / denominator.float;
string => denominator == 1 then numerator.string else "``numerator``/``denominator``";
shared actual Boolean equals(Object that) {
if (is Rational that) {
value simplifiedThis = this.simplified;
value simplifiedThat = that.simplified;
return simplifiedThis.numerator==simplifiedThat.numerator &&
simplifiedThis.denominator==simplifiedThat.denominator;
} else {
return false;
}
}
}
shared Rational? rational(Integer numerator, Integer denominator = 1) =>
if (denominator == 0)
then null
else Rational(numerator, denominator).simplified;
shared Rational numeratorOverOne(Integer numerator) => Rational(numerator);
shared abstract class Operation(String lexeme) of addition | subtraction | multiplication | division {
shared formal Rational perform(Rational left, Rational right);
string => lexeme;
}
shared object addition extends Operation("+") {
perform(Rational left, Rational right) => left + right;
}
shared object subtraction extends Operation("-") {
perform(Rational left, Rational right) => left - right;
}
shared object multiplication extends Operation("*") {
perform(Rational left, Rational right) => left * right;
}
shared object division extends Operation("/") {
perform(Rational left, Rational right) => left / right;
}
shared Operation[] operations = `Operation`.caseValues;
shared interface Expression of NumberExpression | OperationExpression {
shared formal Rational evaluate();
}
shared class NumberExpression(Rational number) satisfies Expression {
evaluate() => number;
string => number.string;
}
shared class OperationExpression(Expression left, Operation op, Expression right) satisfies Expression {
evaluate() => op.perform(left.evaluate(), right.evaluate());
string => "(``left`` ``op`` ``right``)";
}
shared void run() {
value twentyfour = numeratorOverOne(24);
value random = DefaultRandom();
function buildExpressions({Rational*} numbers, Operation* ops) {
assert (is NumberExpression[4] numTuple = numbers.collect(NumberExpression).tuple());
assert (is Operation[3] opTuple = ops.sequence().tuple());
value [a, b, c, d] = numTuple;
value [op1, op2, op3] = opTuple;
value opExp = OperationExpression; // this is just to give it a shorter name
return [
opExp(opExp(opExp(a, op1, b), op2, c), op3, d),
opExp(opExp(a, op1, opExp(b, op2, c)), op3, d),
opExp(a, op1, opExp(opExp(b, op2, c), op3, d)),
opExp(a, op1, opExp(b, op2, opExp(c, op3, d)))
];
}
print("Please enter your 4 numbers to see how they form 24 or enter the letter r for random numbers.");
if (exists line = process.readLine()) {
Rational[] chosenNumbers;
if (line.trimmed.uppercased == "R") {
chosenNumbers = random.elements(1..9).take(4).collect((Integer element) => numeratorOverOne(element));
print("The random numbers are ``chosenNumbers``");
} else {
chosenNumbers = line.split().map(Integer.parse).narrow<Integer>().collect(numeratorOverOne);
}
value expressions = {
for (numbers in chosenNumbers.permutations)
for (op1 in operations)
for (op2 in operations)
for (op3 in operations)
for (exp in buildExpressions(numbers, op1, op2, op3))
if (exp.evaluate() == twentyfour)
exp
};
print("The solutions are:");
expressions.each(print);
}
}