-
Paul A. Rubin authoredPaul A. Rubin authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Set.java 4.30 KiB
package setgame;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
/**
* Set is the container for a game instance.
* @author Paul A. Rubin (rubin@msu.edu)
*/
public final class Set {
/** NCARDS is the number of cards in the game. */
public static final int NCARDS = 81;
/** NFEATURES is the number of features on a card. */
public static final int NFEATURES = 4;
/** NVALUES is the number of values a feature can have. */
public static final int NVALUES = 3;
/** SETSIZE is the number of cards in a set. */
public static final int SETSIZE = 3;
private final int[][] cards; // the card deck
private final HashMap<Integer, HashMap<Integer, HashSet<Integer>>>
hasFeature;
private int found; // number of valid sets found
/**
* Constructor.
*/
public Set() {
cards = new int[NCARDS][NFEATURES];
int[] divisors = new int[NFEATURES + 1];
divisors[0] = 1;
for (int i = 1; i <= NFEATURES; i++) {
divisors[i] = NVALUES * divisors[i - 1];
}
// Fill in the cards.
for (int c = 0; c < NCARDS; c++) {
cards[c][0] = c % divisors[1];
for (int f = 1; f < NFEATURES; f++) {
cards[c][f] = (c % divisors[f + 1]) / divisors[f];
}
}
// Fill in the feature possession map.
hasFeature = new HashMap<>();
for (int f = 0; f < NFEATURES; f++) {
HashMap<Integer, HashSet<Integer>> m = new HashMap<>();
for (int v = 0; v < NVALUES; v++) {
HashSet<Integer> s = new HashSet<>();
for (int c = 0; c < NCARDS; c++) {
if (cards[c][f] == v) {
s.add(c);
}
}
m.put(v, s);
}
hasFeature.put(f, m);
}
}
/**
* Gets the set of cards with a particular value of a particular feature.
* @param feature the feature
* @param value the target value
* @return the set of cards with that value
*/
public Collection<Integer> findCards(final int feature, final int value) {
return new HashSet<>(hasFeature.get(feature).get(value));
}
/**
* Prints a set.
* @param set the set to print
*/
public void printSet(final Collection<Integer> set) {
ArrayList<Integer> list = new ArrayList<>(set);
Collections.sort(list);
for (int c : list) {
System.out.println(Arrays.toString(cards[c]) + " (" + c + ")");
}
}
/**
* Computes all valid sets by brute force (using recursion).
* @return the number of sets found
*/
public int findAllSets() {
found = 0;
HashMap<Integer, HashSet<Integer>> map = new HashMap<>();
for (int f = 0; f < NFEATURES; f++) {
map.put(f, new HashSet<>());
}
enumerate(1, 0, map);
return found;
}
/**
* Recursively enumerates all valid sets.
* @param level the level (1 ... SETSIZE) at which the function is called
* @param start the initial card index
* @param values the accumulated values of all features
*/
private void enumerate(final int level, final int start,
final HashMap<Integer, HashSet<Integer>> values) {
// Check for vacuous cases (level or start index too high).
if (level > SETSIZE || start == NCARDS) {
return;
}
for (int c = start; c < NCARDS; c++) {
// Clone the values map, adding the value of each feature in card c.
HashMap<Integer, HashSet<Integer>> v = new HashMap<>();
for (int f = 0; f < NFEATURES; f++) {
HashSet<Integer> s = new HashSet<>(values.get(f));
s.add(cards[c][f]);
v.put(f, s);
}
if (level == SETSIZE) {
// Final level: check for a valid set and if valid count it.
boolean valid = true;
for (int f = 0; f < NFEATURES; f++) {
if (v.get(f).size() == 2) {
valid = false;
break;
}
}
if (valid) {
found += 1;
}
} else {
// Call enumerate at the next level.
enumerate(level + 1, c + 1, v);
}
}
}
/**
* Gets the value of a particular feature for every card.
* @param feature the feature
* @return the vector of feature values
*/
public int[] getValues(final int feature) {
int[] v = new int[NCARDS];
for (int c = 0; c < NCARDS; c++) {
v[c] = cards[c][feature];
}
return v;
}
}