diff --git a/mastermind/.classpath b/mastermind/.classpath new file mode 100644 index 0000000..e834eb8 --- /dev/null +++ b/mastermind/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/mastermind/.project b/mastermind/.project new file mode 100644 index 0000000..7ba2a68 --- /dev/null +++ b/mastermind/.project @@ -0,0 +1,17 @@ + + + mastermind + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/mastermind/.settings/org.eclipse.core.resources.prefs b/mastermind/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/mastermind/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/mastermind/.settings/org.eclipse.jdt.core.prefs b/mastermind/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..9a7984b --- /dev/null +++ b/mastermind/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=21 diff --git a/mastermind/src/mastermind/One.java b/mastermind/src/mastermind/One.java new file mode 100644 index 0000000..ba9011c --- /dev/null +++ b/mastermind/src/mastermind/One.java @@ -0,0 +1,348 @@ +package mastermind; + +import tester.Tester; +import java.awt.Color; +import java.awt.Image; +import java.util.Random; +import javalib.worldimages.*; +import javalib.funworld.*; +import javalib.worldcanvas.*; + +class Examples { + ILoInt noInt = new MtInt(), + intsOne = new ConsInt(1, new ConsInt(2, new ConsInt(3, noInt))); + + Dot redDot = new Dot(Color.red), + greenDot = new Dot(Color.green), + blueDot = new Dot(Color.blue), + reenDot = new Dot(new Color(255, 255, 0)); + + ILoDot noDot = new MtDot(), + dotsOne = new ConsDot(redDot, new ConsDot(greenDot, new ConsDot(blueDot, noDot)), new Random(0)), + dotsTwo = new ConsDot(redDot, new ConsDot(blueDot, new ConsDot(blueDot, noDot))), + dotsThree = new ConsDot(reenDot, new ConsDot(reenDot, new ConsDot(reenDot, noDot))), + exampleDotsOne = new ConsDot(redDot, new ConsDot(greenDot, new ConsDot(blueDot, new ConsDot(greenDot, new ConsDot(redDot, new ConsDot(greenDot, noDot)))))), + exampleDotsTwo = new ConsDot(redDot, new ConsDot(blueDot, new ConsDot(greenDot, new ConsDot(greenDot, new ConsDot(blueDot, new ConsDot(blueDot, noDot)))))); + + Game exampleGame = new Game(); + + boolean testILoDotLen(Tester t) { + return t.checkExpect(noDot.len(), 0) && t.checkExpect(dotsOne.len(), 3); + } + + boolean testILoDotGetnth(Tester t) { + return t.checkException(new IllegalArgumentException("Index out of bounds."), noDot, "get", 1) + && t.checkException(new IllegalArgumentException("Index out of bounds."), dotsOne, "get", 3) + && t.checkExpect(dotsOne.get(0), redDot) + && t.checkExpect(dotsOne.get(2), blueDot); + } + + boolean testILoDotGen(Tester t) { + return t.checkExpect(noDot.gen(219), noDot) + && t.checkExpect(dotsOne.gen(1), new ConsDot(redDot, noDot)) + && t.checkExpect(dotsOne.gen(3), new ConsDot(greenDot, new ConsDot(greenDot, new ConsDot(blueDot, noDot)))); + } + + boolean testILoDotIn(Tester t) { + return t.checkExpect(dotsOne.in(blueDot), true) + && t.checkExpect(noDot.in(redDot), false) + && t.checkExpect(dotsThree.in(blueDot), false); + } + + boolean testILoIntRemove(Tester t) { + return t.checkExpect(intsOne.remove(0), new ConsInt(2, new ConsInt(3, noInt))) + && t.checkExpect(intsOne.remove(2), new ConsInt(1, new ConsInt(2, noInt))) + && t.checkException(new IllegalArgumentException("Index out of bounds: cannot remove something from nothing."), intsOne, "remove", 3) + && t.checkException(new IllegalArgumentException("Index out of bounds: cannot remove something from nothing."), noInt, "remove", 0) + && t.checkException(new IllegalArgumentException("Indices must be positive."), intsOne, "remove", -2); + } + + boolean testILoIntLen(Tester t) { + return t.checkExpect(intsOne.len(), 3) && t.checkExpect(noInt.len(), 0); + } + + boolean testILoIntSubOne(Tester t) { + return t.checkExpect(intsOne.subOne(), new ConsInt(0, new ConsInt(1, new ConsInt(2, noInt)))); + } + + boolean testILoDotExactIndices(Tester t) { + return t.checkException(new IllegalArgumentException("Empty list doesn't exactly match anything."), noDot, "exactIndices", noDot) + && t.checkExpect(dotsOne.exactIndices(dotsOne), new ConsInt(0, new ConsInt(1, new ConsInt(2, noInt)))) + && t.checkExpect(dotsOne.exactIndices(dotsTwo), new ConsInt(0, new ConsInt(2, new MtInt()))); + } + + boolean testILoDotRemove(Tester t) { + return t.checkException(new IllegalArgumentException("Index out of bounds."), noDot, "remove", 0) + && t.checkExpect(dotsOne.remove(0), new ConsDot(greenDot, new ConsDot(blueDot, noDot))) + && t.checkExpect(dotsTwo.remove(1), new ConsDot(redDot, new ConsDot(blueDot, noDot))); + } + + boolean testILoDotRemoveAll(Tester t) { + return t.checkExpect(dotsOne.removeAll(noInt), dotsOne) + && t.checkExpect(dotsOne.removeAll(new ConsInt(0, new ConsInt(1, new ConsInt(2, noInt)))), noDot) + && t.checkExpect(noDot.removeAll(intsOne), noDot) + && t.checkExpect(dotsTwo.removeAll(new ConsInt(1, new ConsInt(2, noInt))), new ConsDot(redDot, noDot)); + } + + boolean testILoDotCountInexact(Tester t) { return true; } + + /* + boolean testILoDotCompare(Tester t) { + return t.checkExpect(exampleDotsOne.compare(exampleDotsTwo), new Feedback(2, 2)); + }*/ + + boolean testDrawMethods(Tester t) { + WorldCanvas c = new WorldCanvas(1000, 1000); + WorldScene s = new WorldScene(1000, 1000); + return + // c.drawScene(s.placeImageXY(exampleGame.draw(), 0, 0)) + //c.drawScene(s.placeImageXY(redDot.draw(), 100, 100)) + //c.drawScene(s.placeImageXY(dotsOne.draw(), 250, 250)) + //&& c.show(); + true; + } +} + +// A game state. +class Game { + static ILoDot DEFAULTDOTS = new ConsDot(new Dot(Color.RED), new ConsDot(new Dot(Color.GREEN), new ConsDot(new Dot(Color.BLUE), new MtDot()))); + static GameConf DEFAULTCONF = new GameConf(true, 5, 5, DEFAULTDOTS); + + GameConf conf; + int guessesLeft; + ILoGuess guesses; + + Game(GameConf conf, int guessesLeft, ILoGuess guesses) { + this.conf = conf; + this.guessesLeft = guessesLeft; + this.guesses = guesses; + } + + // Convenience constructor using default config. + Game() { this(DEFAULTCONF, DEFAULTCONF.nguesses, new MtGuess()); } + + WorldImage draw() { + return new EmptyImage(); + } +} + +// A game configuration. +class GameConf { + boolean dups; // Whether duplicates are allowed. + int len; // The length of the sequence to be guessed. + int nguesses; // Number of guesses the player is allowed. + ILoDot options; // The dots of which the solution is comprised. + + GameConf(boolean dups, int len, int nguesses, ILoDot options) { + if (len <= 0) + throw new IllegalArgumentException("Length of the solution must be greater than 0."); + if (nguesses <= 0) + throw new IllegalArgumentException("Must provide the player some guesses."); + int oplen = options.len(); + if (oplen <= 0) + throw new IllegalArgumentException("Must have dot options to guess with."); + if (!dups && len > oplen) + throw new IllegalArgumentException("Cant create solution of that length without duplicates."); + + this.dups = dups; + this.len = len; + this.nguesses = nguesses; + this.options = options; + } +} + +// A list of guesses; +interface ILoGuess {} + +class ConsGuess implements ILoGuess { + Guess guess; + ILoGuess nxt; + + ConsGuess(Guess guess, ILoGuess nxt) { + this.guess = guess; + this.nxt = nxt; + } +} + +class MtGuess implements ILoGuess {} + +class Guess { + ILoDot guess; + Feedback feedback; + + Guess(ILoDot guess, Feedback feedback) { + this.guess = guess; + this.feedback = feedback; + } +} + +class Feedback { + int exact, + inexact; + + Feedback(int exact, int inexact) { + this.exact = exact; + this.inexact = inexact; + } + + Feedback add(Feedback other) { + int otherExact = other.exact, + otherInexact = other.inexact; + + return new Feedback(this.exact + otherExact, this.inexact + otherInexact); + } +} + +interface ILoDot { + int len(); // Get length. + Dot get(int n); // Get nth element. + ILoDot remove(int n); // Remove nth element. + ILoDot removeAll(ILoInt indices); // Remove element at each index. + ILoDot gen(int n); // Generate randomized list. + + boolean match(Color col); // Do the colors match? + boolean in(Dot dot); // Is the dot in here? + + Feedback compare(ILoDot other); // Compare two lists & give feedback. + ILoInt exactIndices(ILoDot other); // Get indices of exact matches. + ILoInt exactIndicesHelper(ILoDot other, int i); + int countInexact(ILoDot other); // Get the number of inexact matches. Must be fed exact match-free lists to be accurate. + + WorldImage draw(); // Draw the dots. +} + +class ConsDot implements ILoDot { + Random rand; + Dot dot; + ILoDot nxt; + + ConsDot(Dot dot, ILoDot nxt, Random rand) { + this.rand = rand; + this.dot = dot; + this.nxt = nxt; + } + ConsDot(Dot dot, ILoDot nxt) { this(dot, nxt, new Random()); } + + public ILoDot gen(int n) { + return n <= 0 ? new MtDot() : new ConsDot(this.get(this.rand.nextInt(this.len())), this.gen(n - 1)); + } + public Dot get(int n) { + if (n == 0) return this.dot; + else return this.nxt.get(n - 1); + } + public boolean in(Dot dot) { return dot.equals(this.dot) || this.nxt.in(dot); } + public int len() { return 1 + this.nxt.len(); } + public boolean match(Color col) { return col.equals(this.dot.c); } + + public WorldImage draw() { return new BesideAlignImage(AlignModeY.PINHOLE, this.dot.draw(), this.nxt.draw()); } + + public Feedback compare(ILoDot other) { + if (this.len() != other.len()) throw new IllegalArgumentException("Cannot compare different lengthed lists."); + + ILoInt exactIndices = this.exactIndices(other); + + ILoDot thisWithoutExact = this.removeAll(exactIndices); + ILoDot otherWithoutExact = other.removeAll(exactIndices); + + int exact = exactIndices.len(); + int inexact = thisWithoutExact.countInexact(otherWithoutExact); + + return new Feedback(exact, inexact); + } + + public ILoInt exactIndices(ILoDot other) { return this.exactIndicesHelper(other, 0); } + + public ILoInt exactIndicesHelper(ILoDot other, int i) { + // Stop after reaching the end of the list. + if (i == this.len()) return new MtInt(); + + return this.get(i).equals(other.get(i)) ? new ConsInt(i, this.exactIndicesHelper(other, i + 1)) + : this.exactIndicesHelper(other, i + 1); + } + + public int countInexact(ILoDot other) { + return 0; + } + + public ILoDot remove(int n) { + if (n < 0) throw new IllegalArgumentException("Indices must be positive."); + + if (n == 0) return this.nxt; + else return new ConsDot(this.dot, this.nxt.remove(n - 1)); + } + + public ILoDot removeAll(ILoInt indices) { + if (indices.isEmpty()) return this; + return this.remove(indices.first()).removeAll(indices.rest().subOne()); + } +} + +class MtDot implements ILoDot { + public ILoDot gen(int n) { return new MtDot(); } + public Dot get(int n) { throw new IllegalArgumentException("Index out of bounds."); } + public int len() { return 0; } + public WorldImage draw() { return new EmptyImage(); } + public Feedback compare(ILoDot other) { return new Feedback(0, 0); } + public boolean match(Color col) { return false; } + public boolean in(Dot dot) { return false; } + public ILoInt exactIndices(ILoDot other) { + throw new IllegalArgumentException("Empty list doesn't exactly match anything."); + } + public ILoInt exactIndicesHelper(ILoDot other, int i) { return new MtInt(); } + public int countInexact(ILoDot other) { + return 0; + } + public ILoDot remove(int n) { throw new IllegalArgumentException("Index out of bounds."); } + public ILoDot removeAll(ILoInt indices) { return this; } +} + +class Dot { + static int r = 32; + static OutlineMode outlineMode = OutlineMode.SOLID; + + Color c; + + Dot(Color c) { this.c = c; } + + // Draw the dot. + WorldImage draw() { return new CircleImage(r, outlineMode, this.c); } +} + +// A list of integers. +interface ILoInt { + int len(); // Get the length of the list. + ILoInt remove(int n); // Remove the nth element of the list. + boolean isEmpty(); // Is it empty? + int first(); // Get the first value. + ILoInt rest(); // Get the rest value. + ILoInt subOne(); // Subtract 1 from every int. +} + +class ConsInt implements ILoInt { + int val; + ILoInt nxt; + + ConsInt(int val, ILoInt nxt) { + this.val = val; + this.nxt = nxt; + } + + public int len() { return 1 + this.nxt.len(); } + public ILoInt remove(int n) { + if (n < 0) throw new IllegalArgumentException("Indices must be positive."); + return n == 0 ? this.nxt : new ConsInt(this.val, this.nxt.remove(n - 1)); + } + public boolean isEmpty() { return false; } + public int first() { return this.val; } + public ILoInt rest() { return this.nxt; } + public ILoInt subOne() { return new ConsInt(this.val - 1, this.nxt.subOne()); } +} + +class MtInt implements ILoInt { + public int len() { return 0; } + public ILoInt remove(int n) { throw new IllegalArgumentException("Index out of bounds: cannot remove something from nothing."); } + public boolean isEmpty() { return true; } + public int first() { throw new IllegalArgumentException("Cannot get first element in a list without any."); } + public ILoInt rest() { throw new IllegalArgumentException("Cannot get rest of empty list."); } + public ILoInt subOne() { return this; } +}