chesspresso.game.GameMoveModel Maven / Gradle / Ivy
/*
* Chessplorer-Lib - an open source chess library written in Java
* Copyright (C) 2016 Chessplorer.org
* Copyright (C) 2012-2016 Gerhard Kalab
* Copyright (C) 2002-2003 Bernhard Seybold
*
* This software is published under the terms of the LGPL Software License,
* a copy of which has been included with this distribution in the LICENSE.txt
* file.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
*/
package chesspresso.game;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Vector;
import chesspresso.move.Move;
import chesspresso.position.NAG;
import java.io.ByteArrayOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Representation of moves of a chess game.
*
* @author Bernhard Seybold
* @author Andreas Rudolph
*/
public class GameMoveModel
{
private final static Logger LOGGER = LoggerFactory.getLogger( GameMoveModel.class );
private static final int MIN_ALLOC_SIZE = 262144;
private final static boolean DEBUG = false;
private final static boolean EXTRA_CHECKS = true;
//======================================================================
public final static int
MODE_EVERYTHING = 0;
final static short
NO_MOVE = Move.NO_MOVE,
LINE_START = Move.OTHER_SPECIALS,
LINE_END = Move.OTHER_SPECIALS + 1,
COMMENT_START = Move.OTHER_SPECIALS + 2,
COMMENT_END = Move.OTHER_SPECIALS + 3,
PRE_COMMENT_START = Move.OTHER_SPECIALS + 5,
PRE_COMMENT_END = Move.OTHER_SPECIALS + 6,
NULL_MOVE = Move.NULL_MOVE,
NAG_BASE = Move.OTHER_SPECIALS + 16,
LAST_SPECIAL = (short)(NAG_BASE + NAG.NUM_OF_NAGS);
static {
if (LAST_SPECIAL > Move.SPECIAL_MOVE + Move.NUM_OF_SPECIAL_MOVES) {
throw new RuntimeException("Not enough space to define special moves for game move model");
}
}
//======================================================================
private short[] m_moves;
private int m_size;
private int m_hashCode;
//======================================================================
public GameMoveModel()
{
m_moves = new short[MIN_ALLOC_SIZE];
m_moves[0] = LINE_START;
m_moves[1] = LINE_END;
m_size = 2;
m_hashCode = 0;
}
public GameMoveModel(DataInput in, int mode) throws IOException
{
load(in, mode);
m_hashCode = 0; // TODO: store in file?
}
//======================================================================
// invariant checking
private void checkLegalCursor(int index)
{
if (index < 0) throw new RuntimeException("Illegal index " + index);
if (index >= m_size) throw new RuntimeException("Illegal index " + index + " m_size=" + m_size);
if (m_moves[index] != LINE_START && m_moves[index] != NULL_MOVE && !isMoveValue(m_moves[index]))
throw new RuntimeException("No move at index " + index + " move=" + valueToString(m_moves[index]));
}
//======================================================================
private static boolean isMoveValue(short value) {return !Move.isSpecial(value);}
private static boolean isNagValue(short value) {return value >= NAG_BASE && value < NAG_BASE + NAG.NUM_OF_NAGS;}
private static short getNagForValue(short value) {return (short)(value - NAG_BASE);}
private static short getValueForNag(short nag) {return (short)(nag + NAG_BASE);}
//======================================================================
private void changed()
{
m_hashCode = 0;
}
//======================================================================
public boolean hasNag(int index, short nag)
{
if (DEBUG) {
LOGGER.debug("hasNag " + index + " nag " + nag);
LOGGER.debug( writeToString() );
}
short nagValue = getValueForNag(nag);
short value;
do {
index++;
value = m_moves[index];
if (value == nagValue) return true;
} while (isNagValue(value));
return false;
}
public short[] getNags(int index)
{
if (EXTRA_CHECKS)
if (!isMoveValue(m_moves[index]))
throw new RuntimeException("No move at index " + index + " move=" + valueToString(m_moves[index]));
int num = 0;
while (isNagValue(m_moves[index + 1])) {index++; num++;}
if (num == 0) {
return null;
} else {
short[] nags = new short[num];
// collect nags from back to front (most recently added last)
for (int i = 0; i < num; i++) nags[i] = getNagForValue(m_moves[index - i]);
return nags;
}
}
public void addNag(int index, short nag)
{
if (DEBUG) {
LOGGER.debug("addNag " + index + " nag " + nag);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
if (!isMoveValue(m_moves[index]))
// ignore if there's no move: just don't add the NAG
return;
makeSpace(index + 1, 1, false); // most recent nag first
m_moves[index + 1] = getValueForNag(nag);
changed();
if (DEBUG) LOGGER.debug( writeToString() );
}
public boolean removeNag(int index, short nag)
{
if (DEBUG) {
LOGGER.debug("removeNag " + index + " nag " + nag);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
if (!isMoveValue(m_moves[index]))
throw new RuntimeException("No move at index " + index + " val=" + valueToString(m_moves[index]));
short nagValue = getValueForNag(nag);
short value;
boolean changed = false;
do {
index++;
value = m_moves[index];
if (value == nagValue) {
while (isNagValue(m_moves[index + 1])) {
m_moves[index] = m_moves[index + 1];
index++;
}
m_moves[index] = NO_MOVE;
changed = true;
break;
}
} while (isNagValue(value));
changed();
if (DEBUG) LOGGER.debug( writeToString() );
return changed;
}
//======================================================================
private int skipComment(int index)
{
if (m_moves[index] == COMMENT_START) {
while (m_moves[index] != COMMENT_END) index++;
} else if (m_moves[index] == COMMENT_END) {
while (m_moves[index] != COMMENT_START) index--;
} else if (m_moves[index] == PRE_COMMENT_START) {
while (m_moves[index] != PRE_COMMENT_END) index++;
} else if (m_moves[index] == PRE_COMMENT_END) {
while (m_moves[index] != PRE_COMMENT_START) index--;
} else {
throw new RuntimeException("No comment start or end at index " + index + " move " + valueToString(m_moves[index]));
}
return index;
}
public String getComment(int index)
{
if (EXTRA_CHECKS)
if (!isMoveValue(m_moves[index]) && index != 0) // comment at index 0 allowed
throw new RuntimeException("No move at index " + index + " move=" + valueToString(m_moves[index]));
// skip all nags
while(isNagValue(m_moves[index + 1])) index++;
// skip pre move comments
if (m_moves[index + 1] == PRE_COMMENT_START) {
while (m_moves[index] != PRE_COMMENT_END) index++;
}
if (m_moves[index + 1] == COMMENT_START) {
StringBuffer sb = new StringBuffer();
addCommentToStringBuffer(index, sb, COMMENT_START, COMMENT_END);
return sb.toString();
} else {
return null;
}
}
private void addCommentToStringBuffer(int index, StringBuffer sb, short commentStartToken, short commentEndToken) {
index += 2;
while (m_moves[index] != commentEndToken) {
sb.append((char)m_moves[index]);
index++;
}
if (m_moves[index + 1] == commentStartToken) {
sb.append(" ");
addCommentToStringBuffer(index, sb, commentStartToken, commentEndToken);
}
}
public String getPreMoveComment(int index)
{
if (EXTRA_CHECKS)
if (!isMoveValue(m_moves[index]) && index != 0) // comment at index 0 allowed
throw new RuntimeException("No move at index " + index + " move=" + valueToString(m_moves[index]));
// skip all nags
while(isNagValue(m_moves[index + 1])) index++;
// skip other comments
if (m_moves[index + 1] == COMMENT_START) {
while (m_moves[index] != COMMENT_END) index++;
}
if (m_moves[index + 1] == PRE_COMMENT_START) {
StringBuffer sb = new StringBuffer();
addCommentToStringBuffer(index, sb, PRE_COMMENT_START, PRE_COMMENT_END);
return sb.toString();
} else {
return null;
}
}
public boolean addComment(int index, String comment)
{
if (DEBUG) {
LOGGER.debug("addComment " + index+ " comment " + comment);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
if (index != 0 && !isMoveValue(m_moves[index]))
throw new RuntimeException("No move at index " + index + " val=" + valueToString(m_moves[index]));
if (comment == null || comment.length() == 0) return false; // =====>
// allow comments before first move (index == 0)
if (index != 0) {
while(isNagValue(m_moves[index + 1])) index++;
if (m_moves[index + 1] == COMMENT_START) {
index++;
index = skipComment(index);
}
}
makeSpace(index + 1, comment.length() + 2, false);
m_moves[index + 1] = COMMENT_START;
for (int i = 0; i < comment.length(); i++) {
m_moves[index + 2 + i] = (short)comment.charAt(i);
}
m_moves[index + comment.length() + 2] = COMMENT_END;
changed();
if (DEBUG) LOGGER.debug( writeToString() );
return true;
}
public boolean addPreMoveComment(int index, String comment)
{
if (DEBUG) {
LOGGER.debug("addPreMoveComment " + index+ " comment " + comment);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
if (index != 0 && !isMoveValue(m_moves[index]))
throw new RuntimeException("No move at index " + index + " val=" + valueToString(m_moves[index]));
if (comment == null || comment.length() == 0) return false; // =====>
// allow comments before first move (index == 0)
if (index != 0) {
while(isNagValue(m_moves[index + 1])) index++;
}
makeSpace(index + 1, comment.length() + 2, false);
m_moves[index + 1] = PRE_COMMENT_START;
for (int i = 0; i < comment.length(); i++) {
m_moves[index + 2 + i] = (short)comment.charAt(i);
}
m_moves[index + comment.length() + 2] = PRE_COMMENT_END;
changed();
if (DEBUG) LOGGER.debug( writeToString() );
return true;
}
public boolean removeComment(int index)
{
if (DEBUG) {
LOGGER.debug("removeComment " + index);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
if (index != 0 && !isMoveValue(m_moves[index]))
throw new RuntimeException("No move at index " + index + " val=" + valueToString(m_moves[index]));
// allow comments before first move (index == 0)
if (index != 0) {
while(isNagValue(m_moves[index + 1])) index++;
}
boolean isChanged = false;
if (m_moves[index + 1] == COMMENT_START || m_moves[index + 1] == PRE_COMMENT_START) {
int i = skipComment(index + 1);
if (m_moves[i + 1] == COMMENT_START || m_moves[i + 1] == PRE_COMMENT_START) {
i = skipComment(i + 1);
}
for (; i > index; i--) {
m_moves[i] = NO_MOVE;
}
isChanged = true;
}
if (isChanged) changed();
if (DEBUG) LOGGER.debug( writeToString() );
return isChanged;
}
public boolean setComment(int index, String comment)
{
boolean changed = removeComment(index);
return addComment(index, comment) || changed;
}
//======================================================================
public boolean hasLines()
{
for (int i=1; i= 0 && index < m_size) {
short move = m_moves[index];
return (isMoveValue(move) ? move : NO_MOVE);
} else {
return NO_MOVE;
}
}
/**
*@return -1 if at the beginning of a line
*/
public int goBack(int index, boolean gotoMainLine)
{
if (DEBUG) {
LOGGER.debug("goBack " + index + " " + gotoMainLine);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
checkLegalCursor(index);
if (index <= 0) return -1; // =====>
index--;
int level = 0;
while (index > 0) {
short move = m_moves[index];
if (move == LINE_START) {
level--;
if (level == -1) {
if (!gotoMainLine) {
index = -1; break;
} else {
index = goBack(index, false); // now at main line's move
index = goBack(index, false); // now one move back
break;
}
}
}
else if (move == LINE_END) level++;
else if (isNagValue(move)) ;
else if (move == COMMENT_START || move == PRE_COMMENT_START) ; // error
else if (move == COMMENT_END || move == PRE_COMMENT_END) index = skipComment(index);
else if (move == NO_MOVE) ;
else if (level == 0) break; // =====>
index--;
}
if (DEBUG) LOGGER.debug(" --> " + index);
return index;
}
/**
* Advances one move in the current line.
*
* @param index the index of the current move
*
* @return the index of the next move. If the next move does not exist, the index
* points to a LINE_END, where a next move should be inserted.
*/
public int goForward(int index)
{
if (DEBUG) {
LOGGER.debug("goForward " + index);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
checkLegalCursor(index);
index++;
int level = 0;
while (index < m_size - 1) {
short move = m_moves[index];
if (move == LINE_START) level++;
else if (move == LINE_END) {level--; if (level < 0) break;}
else if (isNagValue(move)) ;
else if (move == COMMENT_START || move == PRE_COMMENT_START) index = skipComment(index);
else if (move == COMMENT_END || move == PRE_COMMENT_END) ; // error
else if (move == NO_MOVE) ;
else if (level == 0) break;
index++;
}
if (DEBUG) LOGGER.debug(" --> " + index);
return index;
}
public int goForward(int index, int whichLine)
{
if (DEBUG) {
LOGGER.debug("goForward " + index + " " + whichLine);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
checkLegalCursor(index);
index = goForward(index);
if (m_moves[index] != LINE_END && whichLine > 0) {
index++;
int level = 0;
while (index < m_size - 1) {
short move = m_moves[index];
if (move == LINE_START) {level++; if (level == 1) whichLine--;}
else if (move == LINE_END) {level--; if (level < 0) break;}
else if (isNagValue(move)) ;
else if (move == COMMENT_START || move == PRE_COMMENT_START) index = skipComment(index);
else if (move == COMMENT_END || move == PRE_COMMENT_END) ; // error
else if (move == NO_MOVE) ;
else if (level == 1 && whichLine == 0) break;
else if (level == 0) {index = -1; break;} // =====> move on level 0 -> not enough lines
index++;
}
}
if (DEBUG) LOGGER.debug(" --> " + index);
return index;
}
public int getNumOfNextMoves(int index)
{
if (DEBUG) {
LOGGER.debug("getNumOfNextMoves " + index);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
checkLegalCursor(index);
index = goForward(index);
if (m_moves[index] == LINE_END) return 0; // =====>
index++;
int numOfMoves = 1;
int level = 0;
short move;
while (index < m_size && level >= 0) {
move = m_moves[index];
if (move == LINE_START) level++;
else if (move == LINE_END) {level--; if (level == 0) numOfMoves++;}
else if (isNagValue(move)) ;
else if (move == COMMENT_START || move == PRE_COMMENT_START) index = skipComment(index);
else if (move == COMMENT_END || move == PRE_COMMENT_END) ; // error
else if (move == NO_MOVE) ;
else if (level == 0) break;
index++;
}
if (DEBUG) LOGGER.debug(" --> " + numOfMoves);
return numOfMoves;
}
public boolean hasNextMove(int index)
{
if (DEBUG) {
LOGGER.debug("hasNextMove " + index);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
checkLegalCursor(index);
boolean nextMove = isMoveValue(m_moves[goForward(index)]);
if (DEBUG) LOGGER.debug(" --> " + nextMove);
return (nextMove);
}
//======================================================================
private int findEarliestNoMove(int index)
{
while (index > 1 && m_moves[index - 1] == NO_MOVE) index--;
return index;
}
private int findLatestNoMove(int index)
{
if (EXTRA_CHECKS)
if (index < 1 || index > m_size)
throw new RuntimeException("Index out of bounds " + index);
else if (m_moves[index] != NO_MOVE)
throw new RuntimeException("Expected no move " + index);
while (index > 0 && m_moves[index - 1] == NO_MOVE) index--;
return index;
}
private void enlarge(int index, int size)
{
if (DEBUG) {
LOGGER.debug("enlarge " + index + " " + size);
LOGGER.debug( writeToString() );
}
short[] newMoves = new short[m_moves.length + size];
System.arraycopy(m_moves, 0, newMoves, 0, index);
System.arraycopy(m_moves, index, newMoves, index + size, m_size - index);
java.util.Arrays.fill(newMoves, index, index + size, NO_MOVE);
m_moves = newMoves;
m_size += size;
if (DEBUG) LOGGER.debug( writeToString() );
}
private void makeSpace(int index, int spaceNeeded, boolean possiblyMakeMore)
{
if (DEBUG) {
LOGGER.debug("makeSpace " + index + " " + spaceNeeded);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
if (index < 1 || index >= m_size)
throw new RuntimeException("Index out of bounds " + index + " size=" + m_size);
for (int i = 0; i < spaceNeeded; i++) {
if (m_moves[index + i] != NO_MOVE) {
// not enough space, make it
if (m_size + spaceNeeded - i >= m_moves.length) {
int size = (spaceNeeded - i < 8 && possiblyMakeMore ? 8 : spaceNeeded - i);
enlarge(index, size);
} else {
System.arraycopy(m_moves, index + i, m_moves, index + spaceNeeded, m_size - (index + i));
java.util.Arrays.fill(m_moves, index + i, index + spaceNeeded, NO_MOVE);
m_size += spaceNeeded - i;
}
break;
}
}
if (DEBUG) LOGGER.debug( writeToString() );
}
public int appendAsRightMostLine(int index, short move)
{
if (DEBUG) {
LOGGER.debug("appendAsRightMostLine " + index + " " + Move.getString(move));
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
checkLegalCursor(index);
if (hasNextMove(index)) {
index = goForward(index); // go to the move for which an alternative is entered
index = goForward(index); // go to the end of all existing lines
index = findEarliestNoMove(index);
makeSpace(index, 3, true);
m_moves[index] = LINE_START;
m_moves[index + 1] = move;
m_moves[findLatestNoMove(index + 2)] = LINE_END;
if (DEBUG) LOGGER.debug( writeToString() );
if (DEBUG) LOGGER.debug(" --> " + index);
changed();
return index + 1;
} else {
index = goForward(index);
index = findEarliestNoMove(index);
makeSpace(index, 1, true);
m_moves[index] = move;
if (DEBUG) LOGGER.debug( writeToString() );
if (DEBUG) LOGGER.debug(" --> " + index);
changed();
return index;
}
}
public void deleteCurrentLine(int index)
{
if (DEBUG) {
LOGGER.debug("deleteCurrentLine " + index);
LOGGER.debug( writeToString() );
}
if (EXTRA_CHECKS)
checkLegalCursor(index);
int level = 0;
boolean deleteLineEnd = false;
// check if we stand at a line start
for (int i=1; i " + index);
return index;
}
//======================================================================
public void load(DataInput in, int mode) throws IOException
{
m_size = in.readInt() + 2;
m_moves = new short[m_size];
byte[] data = new byte[2 * (m_size - 2)];
in.readFully(data);
for (int i = 1; i < m_size - 1; i++) {
// copied from RandomAccesFile.readShort
m_moves[i] = (short)((data[2*i - 2] << 8) | (data[2*i - 1] & 0xFF));
}
m_moves[0] = LINE_START;
m_moves[m_size - 1] = LINE_END;
changed();
if (DEBUG) LOGGER.debug( writeToString() );
}
public void save(DataOutput out, int mode) throws IOException
{
// do not save the guards at index 0 and m_size-1
out.writeInt(m_size - 2);
byte[] data = new byte[2 * (m_size - 2)];
for (int i = 1; i < m_size - 1; i++) {
short m = m_moves[i];
// copied from RandomAccesFile.writeShort
data[2*i - 2] = (byte)((m >>> 8) & 0xFF);
data[2*i - 1] = (byte)((m >>> 0) & 0xFF);
}
out.write(data);
}
//======================================================================
static String valueToString(short value)
{
if (value == LINE_START) return "(";
else if (value == LINE_END) return ")";
else if (value == NO_MOVE) return "NO";
else if (value == COMMENT_START || value == PRE_COMMENT_START) return "{";
else if (value == COMMENT_END || value == PRE_COMMENT_END) return "}";
else if (isNagValue(value)) return "$" + getNagForValue(value);
else return Move.getString(value);
}
public void write(PrintStream out)
{
boolean inComment = false;
for (int i=0; i
if (!(obj instanceof GameMoveModel)) return false; // =====>
GameMoveModel gameMoveModel = (GameMoveModel)obj;
if (gameMoveModel.getHashCode() != getHashCode()) return false; // =====>
int index1 = 0, index2 = 0;
for (;;) {
short move1 = m_moves[index1];
short move2 = gameMoveModel.m_moves[index2];
if (move1 == LINE_END && move2 == LINE_END) return true; // =====>
if (move1 != move2) return false; // =====>
index1 = goForward(index1);
index2 = gameMoveModel.goForward(index2);
}
}
public void deleteCurrentVariation(int index) {
index = gotoVariationStart(index);
int startOfVariation = index;
while (m_moves[index] != LINE_END) {
index = goForward(index);
}
int endOfVariation = index;
for (int i = startOfVariation; i <= endOfVariation; i++) {
m_moves[i] = NO_MOVE;
}
changed();
}
public int promoteVariation(int curMove) {
int index = gotoVariationStart(curMove);
int startOfVariation = index;
int varFirstMoveStart = -1;
int varFirstMoveEnd = -1;
while (m_moves[index] != LINE_END) {
index = goForward(index);
if (varFirstMoveStart == -1) {
// get start and end index of the first move in the variation
// (to be copied to the parent line later)
varFirstMoveStart = index;
// exclusive end of first variation move
varFirstMoveEnd = goForward(varFirstMoveStart);
varFirstMoveStart = varFirstMoveStart - startOfVariation;
varFirstMoveEnd = varFirstMoveEnd - startOfVariation;
}
}
int endOfVariation = index;
int parentMove = goBack(startOfVariation, true);
if (startOfVariation > 0) {
short[] variation = extractLine(startOfVariation, endOfVariation);
Vector otherVariations = extractOtherVariations(goBack(
parentMove, false));
short[] parentLine = extractParentLine(parentMove);
int varFirstMoveLength = varFirstMoveEnd - varFirstMoveStart;
int nextMoveIndex = insertMove(parentMove, variation,
varFirstMoveStart, varFirstMoveLength);
m_moves[nextMoveIndex] = LINE_START;
nextMoveIndex++;
nextMoveIndex = insertVariation(nextMoveIndex, parentLine);
for (short[] otherVariation : otherVariations) {
nextMoveIndex = insertVariation(nextMoveIndex, otherVariation);
}
addMovesToEndOfVariation(nextMoveIndex, variation, varFirstMoveEnd);
changed();
return pack(parentMove);
} else {
return -1;
}
}
private int insertVariation(int nextMoveIndex, short[] variation) {
int spaceNeeded = variation.length;
enlarge(nextMoveIndex, spaceNeeded);
System.arraycopy(variation, 0, m_moves, nextMoveIndex, variation.length);
return nextMoveIndex + variation.length;
}
private short[] extractParentLine(int startParentLine) {
int endParentLine = startParentLine;
while (m_moves[endParentLine] != LINE_END) {
endParentLine = goForward(endParentLine);
}
return extractLine(startParentLine, endParentLine);
}
private short[] extractLine(int start, int end) {
short[] result = new short[end - start + 1];
System.arraycopy(m_moves, start, result, 0, result.length);
for (int i = start; i <= end; i++) {
m_moves[i] = NO_MOVE;
}
return result;
}
/**
* Move other variations of the same move to the end of the game
*
* @param parentMove
*/
private Vector extractOtherVariations(int parentMove) {
int index = -1;
int line = 1;
Vector result = new Vector();
do {
index = goForward(parentMove, line);
if (index != -1 && m_moves[index] != LINE_END) {
// move variation to the end
short[] variation = moveVariationToEnd(index);
result.add(variation);
}
} while (index != -1 && m_moves[index] != LINE_END);
return result;
}
/**
* move the variation at index to the end of the parent line
*
* @param index
*/
private short[] moveVariationToEnd(int index) {
int startOfVariation = gotoVariationStart(index);
while (m_moves[index] != LINE_END) {
index = goForward(index);
}
int endOfVariation = index;
short[] variation = extractLine(startOfVariation, endOfVariation);
return variation;
}
/**
* @return the index of the start of the variation
*/
private int gotoVariationStart(int index) {
while (m_moves[index] != LINE_START) {
index--;
}
return index;
}
/**
* Insert a move from another array
*/
private int insertMove(int destIndex, short[] source, int srcPos, int length) {
makeSpace(destIndex, length + 1, false);
System.arraycopy(source, srcPos, m_moves, destIndex, length);
return destIndex + length + 1;
}
private void addMovesToEndOfVariation(int index, short[] moves,
int sourceStart) {
int spaceNeeded = moves.length - sourceStart;
enlarge(index, spaceNeeded);
System.arraycopy(moves, sourceStart, m_moves, index, moves.length
- sourceStart);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy