/* DroidFish - An Android chess program. Copyright (C) 2011 Peter Ă–sterlund, peterosterlund2@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.petero.droidfish.book; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.petero.droidfish.book.DroidBook.BookEntry; import org.petero.droidfish.gamelogic.ChessParseError; import org.petero.droidfish.gamelogic.Move; import org.petero.droidfish.gamelogic.Piece; import org.petero.droidfish.gamelogic.Position; import org.petero.droidfish.gamelogic.TextIO; import org.petero.droidfish.gamelogic.UndoInfo; final class InternalBook implements IOpeningBook { private static Map<Long, List<BookEntry>> bookMap; private static int numBookMoves = -1; InternalBook() { Thread t = new Thread(new Runnable() { @Override public void run() { initInternalBook(); } }); t.setPriority(Thread.MIN_PRIORITY); t.start(); } static boolean canHandle(String filename) { return filename.length() == 0; } @Override public boolean enabled() { return true; } @Override public List<BookEntry> getBookEntries(Position pos) { initInternalBook(); List<BookEntry> ents = bookMap.get(pos.zobristHash()); if (ents == null) return null; List<BookEntry> ret = new ArrayList<BookEntry>(); for (BookEntry be : ents) { BookEntry be2 = new BookEntry(be.move); be2.weight = Math.sqrt(be.weight) * 100 + 1; ret.add(be2); } return ret; } @Override public void setOptions(BookOptions options) { } private synchronized final void initInternalBook() { if (numBookMoves >= 0) return; // long t0 = System.currentTimeMillis(); bookMap = new HashMap<Long, List<BookEntry>>(); numBookMoves = 0; try { InputStream inStream = getClass().getResourceAsStream("/book.bin"); if (inStream == null) throw new IOException(); List<Byte> buf = new ArrayList<Byte>(8192); byte[] tmpBuf = new byte[1024]; while (true) { int len = inStream.read(tmpBuf); if (len <= 0) break; for (int i = 0; i < len; i++) buf.add(tmpBuf[i]); } inStream.close(); Position startPos = TextIO.readFEN(TextIO.startPosFEN); Position pos = new Position(startPos); UndoInfo ui = new UndoInfo(); int len = buf.size(); for (int i = 0; i < len; i += 2) { int b0 = buf.get(i); if (b0 < 0) b0 += 256; int b1 = buf.get(i+1); if (b1 < 0) b1 += 256; int move = (b0 << 8) + b1; if (move == 0) { pos = new Position(startPos); } else { boolean bad = ((move >> 15) & 1) != 0; int prom = (move >> 12) & 7; Move m = new Move(move & 63, (move >> 6) & 63, promToPiece(prom, pos.whiteMove)); if (!bad) addToBook(pos, m); pos.makeMove(m, ui); } } } catch (ChessParseError ex) { throw new RuntimeException(); } catch (IOException ex) { System.out.println("Can't read opening book resource"); // throw new RuntimeException(); } /* { long t1 = System.currentTimeMillis(); System.out.printf("Book moves:%d (parse time:%.3f)%n", numBookMoves, (t1 - t0) / 1000.0); } */ } /** Add a move to a position in the opening book. */ private final void addToBook(Position pos, Move moveToAdd) { List<BookEntry> ent = bookMap.get(pos.zobristHash()); if (ent == null) { ent = new ArrayList<BookEntry>(); bookMap.put(pos.zobristHash(), ent); } for (int i = 0; i < ent.size(); i++) { BookEntry be = ent.get(i); if (be.move.equals(moveToAdd)) { be.weight++; return; } } BookEntry be = new BookEntry(moveToAdd); ent.add(be); numBookMoves++; } private static int promToPiece(int prom, boolean whiteMove) { switch (prom) { case 1: return whiteMove ? Piece.WQUEEN : Piece.BQUEEN; case 2: return whiteMove ? Piece.WROOK : Piece.BROOK; case 3: return whiteMove ? Piece.WBISHOP : Piece.BBISHOP; case 4: return whiteMove ? Piece.WKNIGHT : Piece.BKNIGHT; default: return Piece.EMPTY; } } }