diff --git a/src/Card.java b/src/Card.java old mode 100755 new mode 100644 index 9eed9a5..546ff01 --- a/src/Card.java +++ b/src/Card.java @@ -1,5 +1,55 @@ public class Card { - // Create the rest of this class yourself + private int quantity; + private int color; + private int shading; + private int shape; + + public Card(int a, int b, int c, int d) { + quantity = fixValue(a); + color = fixValue(b); + shading = fixValue(c); + shape = fixValue(d); + } + + // fixes values outside the range + // by Professor Sommers + private int fixValue(int valueToFix) { + if (valueToFix < 1 || valueToFix > 3) + return (((valueToFix % 3) + 3) % 3) + 1; + else + return valueToFix; + } + + public int getQuantity() { + return quantity; + } + + public int getColor() { + return color; + } + + public int getShading() { + return shading; + } + + public int getShape() { + return shape; + } + + + public boolean isSet(Card card2, Card card3) { + return (quantity + card2.getQuantity() + card3.getQuantity()) % 3 == 0 && + (color + card2.getColor() + card3.getColor()) % 3 == 0 && + (shading + card2.getShading() + card3.getShading()) % 3 == 0 && + (shape + card2.getShape() + card3.getShape()) % 3 == 0; + + } + + public String toString() { + String[][] array = {{"R", "G", "P"}, {"O", "T", "S"}, {"O", "D", "S"}}; + + return quantity + array[0][color - 1] + array[1][shading - 1] + array[2][shape - 1]; + } public boolean equals(Object obj) { Card that = (Card)obj; diff --git a/src/CardTest.java b/src/CardTest.java new file mode 100644 index 0000000..c77e9a5 --- /dev/null +++ b/src/CardTest.java @@ -0,0 +1,64 @@ +import junit.framework.TestCase; + +public class CardTest extends TestCase { + + public void testValues() { + Card c = new Card(1, 2, 3, 1); + + assertEquals(1, c.getQuantity()); + assertEquals(2, c.getColor()); + assertEquals(3, c.getShading()); + assertEquals(1, c.getShape()); + } + + public void testValuesOutOfRange() { + Card c = new Card(7, 9, -4, 0); + + assertEquals(2, c.getQuantity()); + assertEquals(1, c.getColor()); + assertEquals(3, c.getShading()); + assertEquals(1, c.getShape()); + } + + // 1 1 1 + public void testSameSet() { + Card c = new Card(3, 2, 2, 1); + Card c2 = new Card(1, 3, 1, 1); + Card c3 = new Card(2, 1, 3, 1); + + assertTrue(c.isSet(c2, c3)); + } + + // 1 2 3 + public void testRowSet() { + Card c = new Card(3, 1, 2, 3); + Card c2 = new Card(1, 2, 1, 2); + Card c3 = new Card(2, 3, 3, 1); + + assertTrue(c.isSet(c2, c3)); + } + + public void testNoSet() { + Card c = new Card(1, 1, 3, 3); + Card c2 = new Card(1, 2, 1, 1); + Card c3 = new Card(2, 1, 3, 1); + + assertFalse(c.isSet(c2, c3)); + } + + public void testString() { + Card c = new Card(1, 2, 3, 1); + + // Should be 1GSO + assertEquals("1GSO", c.toString()); + + System.out.println(c); + } + + public void testEqual() { + Card c1 = new Card(1, 1, 1, 1); + Card c2 = new Card(1, 1, 1, 1); + + assertEquals(c1, c2); + } +} diff --git a/src/Deck.java b/src/Deck.java old mode 100755 new mode 100644 index ab3a2a3..5af2546 --- a/src/Deck.java +++ b/src/Deck.java @@ -2,10 +2,37 @@ import java.io.FileReader; import java.util.StringTokenizer; import java.util.ArrayList; +import java.util.Collections; +// instance variables must be named cards and nextCardIndex +// for provided constructor to work. public class Deck { - // Implement the rest of this class yourself + private ArrayList cards; + private int nextCardIndex; + // Creates deck with 81 set cards shuffled + public Deck() { + cards = new ArrayList(81); +// while(hasNext()) { +// cards.add(getNext()); +// } + for(int a = 1; a < 4; a++) { + for(int b = 1; b < 4; b++) { + for(int c = 1; c < 4; c++) { + for(int d = 1; d < 4; d++) { + cards.add(new Card(a, b, c, d)); + } + } + } + } + nextCardIndex = 0; + Collections.shuffle(cards); + } + + // Crates deck from file named filename + // Does not shuffle the deck + // Usage in test code and later code + // Deck d = new Deck("example.dot"); public Deck(String filename) { cards = new ArrayList(81); @@ -42,4 +69,20 @@ public Deck(String filename) { throw new RuntimeException("Error while reading file: " + e.toString()); } } + + // Returns next card in deck if there is one or otherwise null + public boolean hasNext() { + return nextCardIndex >= 0 && nextCardIndex < cards.size(); + } + // You can call hasNext() within this method to see if you + // should return a card or return null + public Card getNext() { + if(!(hasNext())) { + return null; + } + else { + nextCardIndex += 1; + return cards.get(nextCardIndex - 1); + } + } } diff --git a/src/DeckTest.java b/src/DeckTest.java new file mode 100644 index 0000000..50e731a --- /dev/null +++ b/src/DeckTest.java @@ -0,0 +1,47 @@ +import junit.framework.TestCase; + +public class DeckTest extends TestCase { + + public void test4cards() { + Deck d = new Deck("4cards.dat"); + assertEquals(4, d.cards.size()); +// System.out.println(d.cards); + } + + public void test0cards() { + //System.out.println(System.getProperty("user.dir")); + Deck d = new Deck("0cards.dat"); + assertEquals(0, d.cards.size()); +// System.out.println(d.cards); + } + + public void testGenCards() { + Deck d = new Deck(); + assertEquals(81, d.cards.size()); +// System.out.println(d.cards); + + int quantity = 0; + int color = 0; + int shading = 0; + int shape = 0; + while(d.hasNext()) { + Card c = d.getNext(); + if(c.getQuantity() == 1) { + quantity++; + } + if(c.getColor() == 1) { + color++; + } + if(c.getShading() == 1) { + shading++; + } + if(c.getShape() == 1) { + shape++; + } + } + assertEquals(27, quantity); + assertEquals(27, color); + assertEquals(27, shading); + assertEquals(27, shape); + } +} diff --git a/src/Game.java b/src/Game.java new file mode 100644 index 0000000..6390312 --- /dev/null +++ b/src/Game.java @@ -0,0 +1,103 @@ +public class Game { + Table t; + Deck d; + + Game() { + t = new Table(); + d = new Deck(); + for(int i = 0; i < 12; i++) { + if(d.hasNext()) { + t.add(d.getNext()); + } + else { + break; + } + } + } + + Game(String deck) { + t = new Table(); + d = new Deck(deck); + for(int i = 0; i < 12; i++) { + if(d.hasNext()) { + t.add(d.getNext()); + } + else { + break; + } + } + } + + public int numSets() { + return t.numSets(); + } + + public int numCards() { + return t.numCards(); + } + + public void playRound() { + if(numSets() == 0 && d.hasNext()) { + //System.out.println("t0"); + if(d.hasNext()) { + t.add(d.getNext()); + } + if(d.hasNext()) { + t.add(d.getNext()); + } + if(d.hasNext()) { + t.add(d.getNext()); + } + return; + } + + if(d.hasNext() && numCards() == 12 && numSets() > 0) { + //remove one set and add 3 cards + findSet(); + if(d.hasNext()) { + //System.out.println("t1"); + t.add(d.getNext()); + } + if(d.hasNext()) { + //System.out.println("t2"); + t.add(d.getNext()); + } + if(d.hasNext()) { + //System.out.println("t3"); + t.add(d.getNext()); + } + return; + } + + if(numCards() > 12 && numSets() > 0) { + //System.out.println("t4"); + //remove one set but don't add 3 cards + findSet(); + return; + } + + if(numSets() > 0 && d.hasNext() == false) { + //System.out.println("t5"); + //remove one set + findSet(); + return; + } + } + + private void findSet() { + for(int a = 0; a < t.numCards() - 2; a++) { + for(int b = a + 1; b < t.numCards() - 1; b++) { + for(int c = b + 1; c < t.numCards(); c++) { + if(t.getCard(a).isSet(t.getCard(b), t.getCard(c))) { + t.removeSet(t.getCard(a), t.getCard(b), t.getCard(c)); + return; + } + } + } + } + } + + public boolean isGameOver() { + return d.hasNext() == false && numSets() == 0; + } +} \ No newline at end of file diff --git a/src/GameTest.java b/src/GameTest.java new file mode 100644 index 0000000..afd192b --- /dev/null +++ b/src/GameTest.java @@ -0,0 +1,106 @@ +import junit.framework.TestCase; + +/** + * A JUnit test case class. + * Every method starting with the word "test" will be called when running + * the test with JUnit. + */ +public class GameTest extends TestCase { + + /** + * A test method. + * (Replace "X" with a name describing the test. You may write as + * many "testSomething" methods in this class as you wish, and each + * one will be called when running JUnit over this class.) + */ + public void testGameMethods() { + Game g = new Game("4cards.dat"); + assertEquals(4, g.numCards()); + assertEquals(0, g.numSets()); + + Game g2 = new Game("16cards.dat"); + assertEquals(12, g2.numCards()); + assertEquals(4, g2.numSets()); + + Game g3 = new Game("0cards.dat"); + assertEquals(0, g3.numCards()); + assertEquals(0, g3.numSets()); + + Game g4 = new Game(); + assertEquals(12, g4.numCards()); + + Game g5 = new Game("12cards0setsMileStone.dat"); + assertEquals(12, g5.numCards()); + assertEquals(0, g5.numSets()); + } + + public void testOneRound() { + Game g = new Game("4cards.dat"); + g.playRound(); + assertTrue(g.isGameOver()); + + Game g2 = new Game("0cards.dat"); + g2.playRound(); + assertTrue(g2.isGameOver()); + + Game g3 = new Game("16cards.dat"); + g3.playRound(); + assertFalse(g3.isGameOver()); + + Game g4 = new Game(); + g4.playRound(); + assertFalse(g4.isGameOver()); + + // This test only passes with a modified file that adds 3 extra cards + Game g5 = new Game("12cards0setsMileStone.dat"); + g5.playRound(); + assertEquals(15, g5.numCards()); + + } + + public void testAllRounds() { + Game g = new Game("4cards.dat"); + g.playRound(); + assertTrue(g.isGameOver()); + while(!(g.isGameOver())) { + g.playRound(); + } + assertTrue(g.isGameOver()); + + Game g2 = new Game("0cards.dat"); + g2.playRound(); + assertTrue(g2.isGameOver()); + while(!(g2.isGameOver())) { + g2.playRound(); + } + assertTrue(g2.isGameOver()); + + Game g3 = new Game("16cards.dat"); + g3.playRound(); + assertFalse(g3.isGameOver()); + while(!(g3.isGameOver())) { + g3.playRound(); + } + assertTrue(g3.isGameOver()); + + + Game g4 = new Game(); + while(!(g4.isGameOver())) { + g4.playRound(); + } + assertTrue(g4.isGameOver()); + + Game g5 = new Game("12cards0setsMilestone.dat"); + while(!(g5.isGameOver())) { + g5.playRound(); + } + assertTrue(g4.isGameOver()); + + Game g6 = new Game("12cards14setsMilestone.dat"); + while(!(g6.isGameOver())) { + g6.playRound(); + } + assertTrue(g6.isGameOver()); + + } +} diff --git a/src/MonteCarloSimulation.java b/src/MonteCarloSimulation.java new file mode 100644 index 0000000..5bd3ea1 --- /dev/null +++ b/src/MonteCarloSimulation.java @@ -0,0 +1,31 @@ +public class MonteCarloSimulation { + + public static void main(String[] args) { + int games = 0; + double totalSets = 0; + double totalCards = 0; + double avgNumSets = 0; + double avgNumCards = 0; + + for(int i = 0; i < 10000; i++) { + Game game = new Game(); + games += 1; + + // What is the average number of sets in a random collection of 12 cards from the deck? + totalSets += game.numSets(); + avgNumSets = totalSets / games; + + + while(!(game.isGameOver())) { + game.playRound(); + } + + //What is the average number of cards left at the end of the game? + totalCards += game.numCards(); + avgNumCards = totalCards / games; + } + + System.out.println(avgNumSets); + System.out.println(avgNumCards); + } +} \ No newline at end of file diff --git a/src/SetProject.pdf b/src/SetProject.pdf new file mode 100644 index 0000000..e9c7714 Binary files /dev/null and b/src/SetProject.pdf differ diff --git a/src/Table.java b/src/Table.java new file mode 100644 index 0000000..fb205d6 --- /dev/null +++ b/src/Table.java @@ -0,0 +1,104 @@ +public class Table { + private TableNode head; + + public Table() { + head = null; + } + + public void add(Card card) { + TableNode newNode = new TableNode(card); + newNode.setNext(head); + head = newNode; + } + + public void removeSet(Card card1, Card card2, Card card3) { + if(!(card1.isSet(card2, card3))) { + return; + } + if(head == null) { + return; + } + if(!(findMatch(card1) && findMatch(card2) && findMatch(card3))) { + return; + } + removeCard(card1); + removeCard(card2); + removeCard(card3); + } + + public void removeCard(Card card) { + TableNode prev = null; + TableNode curr = head; + if(head.getCard().equals(card)) { + head = head.getNext(); + return; + } + while(curr != null) { + if(curr.getCard().equals(card)) { + prev.setNext(curr.getNext()); + return; + } + prev = curr; + curr = curr.getNext(); + } + } + + // Check if card is in table + public boolean findMatch(Card card) { + for(int i = 0; i < numCards(); i++) { + if(getCard(i).equals(card)) { + //System.out.println("Match found"); + return true; + } + } + //System.out.println("Match not found"); + return false; + } + + public Card findPrev(Card card) { + for(int i = 0; i < numCards(); i++) { + if(getCard(i) == card) { + return getCard(i - 1); + } + } + return null; + } + + public int numCards() { + int count = 0; + TableNode node = head; + while(node != null) { + count += 1; + node = node.getNext(); + } + return count; + } + + public Card getCard(int index) { + TableNode loc = head; + if(index < numCards() && index >= 0) { + for(int i = 0; i < index; i++) { + loc = loc.getNext(); + } + return loc.getCard(); + } + else { + return null; + } + } + + // 1 and 2 prevent repeats + public int numSets() { + int numSets = 0; + for(int a = 0; a < numCards() - 2; a++) { + for(int b = a + 1; b < numCards() - 1; b++) { + for(int c = b + 1; c < numCards(); c++) { + if(getCard(a).isSet(getCard(b), getCard(c))) { + numSets++; + } + } + } + } + return numSets; + } +} diff --git a/src/TableNode.java b/src/TableNode.java new file mode 100644 index 0000000..acee059 --- /dev/null +++ b/src/TableNode.java @@ -0,0 +1,21 @@ +public class TableNode { + private Card card; + private TableNode next; + + public TableNode(Card a) { + card = a; + next = null; + } + + public TableNode getNext() { + return next; + } + + public void setNext(TableNode newNext) { + next = newNext; + } + + public Card getCard() { + return card; + } +} diff --git a/src/TableNodeTest.java b/src/TableNodeTest.java new file mode 100644 index 0000000..4fbede8 --- /dev/null +++ b/src/TableNodeTest.java @@ -0,0 +1,31 @@ +import junit.framework.TestCase; + +/** + * A JUnit test case class. + * Every method starting with the word "test" will be called when running + * the test with JUnit. + */ +public class TableNodeTest extends TestCase { + + /** + * A test method. + * (Replace "X" with a name describing the test. You may write as + * many "testSomething" methods in this class as you wish, and each + * one will be called when running JUnit over this class.) + */ + + public void testSingleNode() { + TableNode node = new TableNode(null); + assertEquals(null, node.getNext()); + assertEquals(null, node.getCard()); + } + + public void testTwoNodes() { + TableNode node = new TableNode(null); + Card card = new Card(3, 1, 1, 2); + TableNode node2 = new TableNode(card); + node.setNext(node2); + assertEquals(node2, node.getNext()); + assertEquals(card, node2.getCard()); + } +} \ No newline at end of file diff --git a/src/TableTest.java b/src/TableTest.java new file mode 100644 index 0000000..c48918c --- /dev/null +++ b/src/TableTest.java @@ -0,0 +1,107 @@ +import junit.framework.TestCase; + +/** + * A JUnit test case class. + * Every method starting with the word "test" will be called when running + * the test with JUnit. + */ +public class TableTest extends TestCase { + + /** + * A test method. + * (Replace "X" with a name describing the test. You may write as + * many "testSomething" methods in this class as you wish, and each + * one will be called when running JUnit over this class.) + */ + + public void testEmptyTable() { + Table table = new Table(); + assertEquals(0, table.numCards()); + assertEquals(null, table.getCard(0)); + } + + public void testSingleNode() { + Table table = new Table(); + Card card = new Card(2, 1, 1, 3); + table.add(card); + assertEquals(1, table.numCards()); + assertEquals(card, table.getCard(0)); + assertTrue(table.findMatch(card)); + } + + public void testMultipleNodes() { + Table table = new Table(); + Card card1 = new Card(2, 1, 1, 3); + Card card2 = new Card(1, 3, 1, 2); + Card card3 = new Card(2, 1, 3, 3); + // Card 4 is not added to the table + Card card4 = new Card(2, 1, 2, 3); + table.add(card1); + table.add(card2); + table.add(card3); + assertEquals(3, table.numCards()); + assertEquals(card3, table.getCard(0)); + assertTrue(table.findMatch(card3)); + assertFalse(table.findMatch(card4)); + } + + public void testRemoveSet() { + Table table = new Table(); + Card card1 = new Card(2, 1, 1, 3); + Card card2 = new Card(1, 3, 2, 2); + Card card3 = new Card(3, 2, 3, 1); + Card card4 = new Card(2, 2, 1, 3); + Card card5 = new Card(1, 1, 3, 3); + // Card 6 is not added to the table + Card card6 = new Card(2, 1, 2, 3); + table.add(card1); + table.add(card2); + table.add(card3); + table.add(card4); + table.add(card5); + //assertEquals(card4, table.findPrev(card3)); + table.removeSet(card1, card2, card3); + assertEquals(2, table.numCards()); + } + + // Card starting at beginning of list + public void testRemoveSet2() { + Table table = new Table(); + Card card1 = new Card(1, 3, 2, 2); + Card card2 = new Card(3, 2, 3, 1); + Card card3 = new Card(2, 2, 1, 3); + Card card4 = new Card(1, 1, 3, 3); + Card card5 = new Card(2, 1, 1, 3); + // Card 6 is not added to the table + Card card6 = new Card(2, 1, 2, 3); + table.add(card1); + table.add(card2); + table.add(card3); + table.add(card4); + table.add(card5); + //assertEquals(card4, table.findPrev(card3)); + table.removeSet(card1, card2, card5); + assertEquals(2, table.numCards()); + } + + // Assuming sets are not removed + public void testnumSets() { + Table table = new Table(); + Card card1 = new Card(1, 1, 1, 1); + Card card2 = new Card(2, 2, 2, 2); + Card card3 = new Card(3, 3, 3, 3); + Card card4 = new Card(1, 2, 3, 1); + Card card5 = new Card(2, 3, 1, 2); + Card card6 = new Card(3, 1, 2, 3); + + table.add(card1); + table.add(card2); + table.add(card3); + table.add(card4); + table.add(card5); + table.add(card6); + + assertEquals(2, table.numSets()); + + } +} \ No newline at end of file diff --git a/src/records.rtf b/src/records.rtf new file mode 100644 index 0000000..e388a7f --- /dev/null +++ b/src/records.rtf @@ -0,0 +1,44 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1347\cocoasubrtf570 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 ArialMT;} +{\colortbl;\red255\green255\blue255;\red0\green0\blue0;} +\margl1440\margr1440\vieww16560\viewh19580\viewkind0 +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural + +\f0\fs24 \cf0 10,000x\ +2.7873\ +2.7883\ +2.7981\ +2.8012\ +2.7876\ +avg 2.7925\ +\ +10,000x\ +7.7022\ +7.7022\ +7.6791\ +7.6866\ +7.6848\ +avg 7.69098\ +\ +\ +100k\ +2.78221\ +2.77845\ +2.7921\ +2.78785\ +2.78283\ +avg +\f1\fs30 \cf2 \expnd0\expndtw0\kerning0 +\outl0\strokewidth0 \strokec2 2.784688 +\f0\fs24 \cf0 \kerning1\expnd0\expndtw0 \outl0\strokewidth0 \ +\ +\ +100k\ +7.68045\ +7.69215\ +7.6851\ +7.69092\ +7.69068\ +avg +\f1\fs30 \cf2 \expnd0\expndtw0\kerning0 +\outl0\strokewidth0 \strokec2 7.68786} \ No newline at end of file