/* * Copyright 2015 S. Webber * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.oakgp.examples.tictactoe; import static java.util.EnumSet.allOf; import static java.util.EnumSet.copyOf; import static java.util.EnumSet.noneOf; import static java.util.EnumSet.of; import static org.oakgp.examples.tictactoe.Move.BOTTOM_CENTRE; import static org.oakgp.examples.tictactoe.Move.BOTTOM_LEFT; import static org.oakgp.examples.tictactoe.Move.BOTTOM_RIGHT; import static org.oakgp.examples.tictactoe.Move.CENTRE; import static org.oakgp.examples.tictactoe.Move.MIDDLE_LEFT; import static org.oakgp.examples.tictactoe.Move.MIDDLE_RIGHT; import static org.oakgp.examples.tictactoe.Move.TOP_CENTRE; import static org.oakgp.examples.tictactoe.Move.TOP_LEFT; import static org.oakgp.examples.tictactoe.Move.TOP_RIGHT; import static org.oakgp.examples.tictactoe.Symbol.X; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Optional; final class Board { private static final int MAX_MOVES = Move.values().length; private static final List<EnumSet<Move>> LINES = new ArrayList<>(); static { // horizontal LINES.add(of(TOP_LEFT, TOP_CENTRE, TOP_RIGHT)); LINES.add(of(MIDDLE_LEFT, CENTRE, MIDDLE_RIGHT)); LINES.add(of(BOTTOM_LEFT, BOTTOM_CENTRE, BOTTOM_RIGHT)); // vertical LINES.add(of(TOP_LEFT, MIDDLE_LEFT, BOTTOM_LEFT)); LINES.add(of(TOP_CENTRE, CENTRE, BOTTOM_CENTRE)); LINES.add(of(TOP_RIGHT, MIDDLE_RIGHT, BOTTOM_RIGHT)); // diagonal LINES.add(of(TOP_LEFT, CENTRE, BOTTOM_RIGHT)); LINES.add(of(TOP_RIGHT, CENTRE, BOTTOM_LEFT)); } private static final EnumSet<Move> ALL = allOf(Move.class); private static final EnumSet<Move> NONE = noneOf(Move.class); private static final EnumSet<Move> CORNERS = of(TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT); private static final EnumSet<Move> SIDES = of(TOP_CENTRE, MIDDLE_RIGHT, BOTTOM_CENTRE, BOTTOM_LEFT); private final EnumSet<Move> oState; private final EnumSet<Move> xState; Board() { this(NONE, NONE); } private Board(EnumSet<Move> oState, EnumSet<Move> xState) { this.oState = oState; this.xState = xState; } Board update(Symbol symbol, Move move) { if (isInvalid(move)) { throw new IllegalArgumentException("Invalid move:" + move + " o: " + oState + " x: " + xState); } else if (symbol == X) { return new Board(oState, copyAndAdd(xState, move)); } else { return new Board(copyAndAdd(oState, move), xState); } } private EnumSet<Move> copyAndAdd(EnumSet<Move> original, Move move) { EnumSet<Move> updatedMoves = copyOf(original); updatedMoves.add(move); return updatedMoves; } private boolean isInvalid(Move move) { return !isFree(move) || isWinner(xState) || isWinner(oState) || isDraw(); } boolean isDraw() { return oState.size() + xState.size() == MAX_MOVES; } boolean isWinner(Symbol symbol) { return isWinner(getState(symbol)); } boolean isWinner(EnumSet<Move> moves) { return LINES.stream().filter(l -> getIntersectionSize(l, moves) == 3).findFirst().isPresent(); } Move getWinningMove(Symbol symbol) { EnumSet<Move> moves = getState(symbol); EnumSet<Move> opponents = getState(symbol.getOpponent()); Optional<EnumSet<Move>> o = LINES.stream().filter(l -> getIntersectionSize(l, moves) == 2 && getIntersectionSize(l, opponents) == 0).findFirst(); return o.map(l -> first(difference(l, moves))).orElse(null); } Move getFreeCorner() { return getFree(CORNERS); } Move getFreeSide() { return getFree(SIDES); } Move getFreeCentre() { return isFree(CENTRE) ? CENTRE : null; } Move getFreeMove() { return getFree(ALL); } boolean isOccupied(Move m, Symbol s) { return getState(s).contains(m); } boolean isFree(Move m) { return !oState.contains(m) && !xState.contains(m); } private Move getFree(EnumSet<Move> possibles) { return possibles.stream().filter(this::isFree).findFirst().orElse(null); } private int getIntersectionSize(EnumSet<Move> a, EnumSet<Move> b) { return intersection(a, b).size(); } private EnumSet<Move> intersection(EnumSet<Move> a, EnumSet<Move> b) { EnumSet<Move> intersection = copyOf(a); intersection.retainAll(b); return intersection; } private EnumSet<Move> difference(EnumSet<Move> a, EnumSet<Move> b) { EnumSet<Move> intersection = copyOf(a); intersection.removeAll(b); return intersection; } private Move first(EnumSet<Move> s) { return s.iterator().next(); } private EnumSet<Move> getState(Symbol s) { return s == X ? xState : oState; } }