Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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;
}
}