Index: opponentmodel/src/main/java/geniusweb/opponentmodel/FrequencyOpponentModel.java
===================================================================
--- opponentmodel/src/main/java/geniusweb/opponentmodel/FrequencyOpponentModel.java	(revision 28)
+++ opponentmodel/src/main/java/geniusweb/opponentmodel/FrequencyOpponentModel.java	(revision 29)
@@ -2,4 +2,5 @@
 
 import java.math.BigDecimal;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -35,4 +36,5 @@
 	private final Map<String, Map<Value, Integer>> bidFrequencies;
 	private final BigDecimal totalBids;
+	private final Bid resBid;
 
 	public FrequencyOpponentModel() {
@@ -40,13 +42,5 @@
 		this.bidFrequencies = null;
 		this.totalBids = BigDecimal.ZERO;
-	}
-
-	@Override
-	public FrequencyOpponentModel with(Domain domain, Bid resBid) {
-		// FIXME merge already available frequencies?
-		return new FrequencyOpponentModel(domain,
-				domain.getIssues().stream().collect(
-						Collectors.toMap(iss -> iss, iss -> new HashMap<>())),
-				BigDecimal.ZERO);
+		this.resBid = null;
 	}
 
@@ -62,17 +56,35 @@
 	 *               {@link #bidFrequencies} for each issue (this is not
 	 *               checked).
+	 * @param resBid the reservation bid. Can be null
 	 */
 	private FrequencyOpponentModel(Domain domain,
-			Map<String, Map<Value, Integer>> freqs, BigDecimal total) {
-		if (domain == null) {
-			throw new NullPointerException("domain=null");
+			Map<String, Map<Value, Integer>> freqs, BigDecimal total,
+			Bid resBid) {
+		if (domain == null) {
+			throw new IllegalStateException("domain is not initialized");
 		}
 		this.domain = domain;
 		this.bidFrequencies = freqs;
 		this.totalBids = total;
+		this.resBid = resBid;
+	}
+
+	@Override
+	public FrequencyOpponentModel with(Domain domain, Bid resBid) {
+		if (domain == null) {
+			throw new NullPointerException("domain is not initialized");
+		}
+		// FIXME merge already available frequencies?
+		return new FrequencyOpponentModel(domain,
+				domain.getIssues().stream().collect(
+						Collectors.toMap(iss -> iss, iss -> new HashMap<>())),
+				BigDecimal.ZERO, resBid);
 	}
 
 	@Override
 	public BigDecimal getUtility(Bid bid) {
+		if (domain == null) {
+			throw new IllegalStateException("domain is not initialized");
+		}
 		String err = domain.isComplete(bid);
 		if (err != null) {
@@ -93,24 +105,8 @@
 	@Override
 	public String getName() {
+		if (domain == null) {
+			throw new IllegalStateException("domain is not initialized");
+		}
 		return "FreqOppModel" + (serial++) + "For" + domain;
-	}
-
-	/**
-	 * 
-	 * @param issue the issue to check
-	 * @param value the value to check
-	 * @return the fraction of the total cases that bids contained given value
-	 *         for the issue.
-	 */
-	private BigDecimal getFraction(String issue, Value value) {
-		if (totalBids == BigDecimal.ZERO) {
-			return BigDecimal.ONE;
-		}
-		Integer freq = bidFrequencies.get(issue).get(value);
-		if (freq == null) {
-			freq = 0;
-		}
-		return new BigDecimal(freq).divide(totalBids, DECIMALS,
-				BigDecimal.ROUND_HALF_UP);
 	}
 
@@ -122,4 +118,8 @@
 	@Override
 	public FrequencyOpponentModel with(Action action, Progress progress) {
+		if (domain == null) {
+			throw new IllegalStateException("domain is not initialized");
+		}
+
 		if (!(action instanceof Offer))
 			return this;
@@ -140,5 +140,41 @@
 
 		return new FrequencyOpponentModel(domain, newFreqs,
-				totalBids.add(BigDecimal.ONE));
+				totalBids.add(BigDecimal.ONE), resBid);
+	}
+
+	/**
+	 * 
+	 * @param issue the issue to get frequency info for
+	 * @return a map containing a map of values and the number of times that
+	 *         value was used in previous bids. Values that are possible but not
+	 *         in the map have frequency 0.
+	 */
+	public Map<Value, Integer> getCounts(String issue) {
+		if (domain == null) {
+			throw new IllegalStateException("domain is not initialized");
+		}
+		if (!(bidFrequencies.containsKey(issue))) {
+			return Collections.emptyMap();
+		}
+		return Collections.unmodifiableMap(bidFrequencies.get(issue));
+	}
+
+	/**
+	 * 
+	 * @param issue the issue to check
+	 * @param value the value to check
+	 * @return the fraction of the total cases that bids contained given value
+	 *         for the issue.
+	 */
+	private BigDecimal getFraction(String issue, Value value) {
+		if (totalBids == BigDecimal.ZERO) {
+			return BigDecimal.ONE;
+		}
+		Integer freq = bidFrequencies.get(issue).get(value);
+		if (freq == null) {
+			freq = 0;
+		}
+		return new BigDecimal(freq).divide(totalBids, DECIMALS,
+				BigDecimal.ROUND_HALF_UP);
 	}
 
@@ -159,6 +195,5 @@
 	@Override
 	public Bid getReservationBid() {
-		// don't throw but we really have no clue.
-		return new Bid(new HashMap<>());
+		return resBid;
 	}
 
Index: opponentmodel/src/main/java/geniusweb/opponentmodel/OpponentModel.java
===================================================================
--- opponentmodel/src/main/java/geniusweb/opponentmodel/OpponentModel.java	(revision 28)
+++ opponentmodel/src/main/java/geniusweb/opponentmodel/OpponentModel.java	(revision 29)
@@ -26,6 +26,9 @@
 	 * constructing an instance. It can also be called later, if there is a
 	 * change in the domain or resBid.
+	 * <p>
+	 * This late-initialization is to support boa models that have late
+	 * initialization.
 	 * 
-	 * @param domain the domain to work with
+	 * @param domain the domain to work with. Must be not null.
 	 * @param resBid the reservation bid, or null if no reservationbid is
 	 *               available.
Index: opponentmodel/src/test/java/geniusweb/profile/opponentmodel/FrequencyOppModelTest.java
===================================================================
--- opponentmodel/src/test/java/geniusweb/profile/opponentmodel/FrequencyOppModelTest.java	(revision 28)
+++ opponentmodel/src/test/java/geniusweb/profile/opponentmodel/FrequencyOppModelTest.java	(revision 29)
@@ -8,4 +8,5 @@
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -35,4 +36,6 @@
 	private static final DiscreteValue I1V1 = new DiscreteValue("i1v1");
 	private static final DiscreteValue I1V2 = new DiscreteValue("i1v2");
+	private static final DiscreteValue I2V1 = new DiscreteValue("i2v1");
+	private static final DiscreteValue I2V2 = new DiscreteValue("i2v2");
 	private static final DiscreteValue I1V2b = new DiscreteValue("i1v2b");
 	private static final Progress progress = mock(Progress.class);
@@ -162,3 +165,40 @@
 	}
 
+	@Test
+	public void testGetCounts() {
+		assertEquals(Collections.emptyMap(), oppModel1.getCounts(ISS1));
+		assertEquals(Collections.emptyMap(), oppModel1.getCounts(ISS2));
+
+		Map<String, Value> values1 = new HashMap<>();
+		values1.put(ISS1, I1V1);
+		values1.put(ISS2, I2V1);
+		Offer offer1 = new Offer(other, new Bid(values1));
+
+		FrequencyOpponentModel oppmod = oppModel1.with(offer1, progress);
+
+		assertEquals(1, oppmod.getCounts(ISS1).get(I1V1).intValue());
+		assertEquals(null, oppmod.getCounts(ISS1).get(I1V2));
+		assertEquals(1, oppmod.getCounts(ISS2).get(I2V1).intValue());
+		assertEquals(null, oppmod.getCounts(ISS2).get(I2V2));
+
+		// same identical bid. Counts should go to 2
+		oppmod = oppmod.with(offer1, progress);
+
+		assertEquals(2, oppmod.getCounts(ISS1).get(I1V1).intValue());
+		assertEquals(2, oppmod.getCounts(ISS2).get(I2V1).intValue());
+
+		Map<String, Value> values2 = new HashMap<>();
+		values2.put(ISS1, I1V2);
+		values2.put(ISS2, I2V2);
+		Offer offer2 = new Offer(other, new Bid(values2));
+
+		// Other bid. with value2 for both issues
+		oppmod = oppmod.with(offer2, progress);
+
+		assertEquals(2, oppmod.getCounts(ISS1).get(I1V1).intValue());
+		assertEquals(2, oppmod.getCounts(ISS2).get(I2V1).intValue());
+		assertEquals(1, oppmod.getCounts(ISS1).get(I1V2).intValue());
+		assertEquals(1, oppmod.getCounts(ISS2).get(I2V2).intValue());
+
+	}
 }
