// SgfUtil.java
package net.sf.gogui.sgf;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sf.gogui.game.TimeSettings;
public final class SgfUtil
{
/** Result of parseOvertime(). */
public static final class Overtime
{
public long m_byoyomi;
public int m_byoyomiMoves;
}
/** Format byoyomi information for OT property.
The format is "N moves / S min" or "N moves / S sec"
This format is also recognized by parseOvertime.
Returns null, if timeSettings does not define byoyomi */
public static String getOvertime(TimeSettings timeSettings)
{
if (! timeSettings.getUseByoyomi())
return null;
StringBuilder result = new StringBuilder();
int byoyomiMoves = timeSettings.getByoyomiMoves();
long byoyomi = timeSettings.getByoyomi();
result.append(byoyomiMoves);
result.append(" moves / ");
if (byoyomi % 60000 == 0)
{
result.append(byoyomi / 60000L);
result.append(" min");
}
else
{
result.append(byoyomi / 1000L);
result.append(" sec");
}
return result.toString();
}
public static Overtime parseOvertime(String value)
{
value = value.trim();
Overtime result = null;
/* Used by SgfWriter */
result =
parseOvertime(value, "(\\d+)\\s*moves\\s*/\\s*(\\d+)\\s*sec",
true, 1000L);
if (result != null)
return result;
/* Used by Smart Go */
result =
parseOvertime(value, "(\\d+)\\s*moves\\s*/\\s*(\\d+)\\s*min",
true, 60000L);
if (result != null)
return result;
/* Used by Quarry, CGoban 2 */
result =
parseOvertime(value, "(\\d+)/(\\d+)\\s*canadian", true, 1000L);
if (result != null)
return result;
return result;
}
/** Parse value of TM property.
According to FF4, TM needs to be a real value, but older SGF versions
allow a string with unspecified content. We try to parse a few known
formats.
@return The (pre-byoyomi-)time in milliseconds or -1, if the
format was not recognized */
public static long parseTime(String value)
{
value = value.trim();
try
{
return (long)(Double.parseDouble(value) * 1000);
}
catch (NumberFormatException e1)
{
}
try
{
Pattern pattern;
Matcher matcher;
// Pattern as written by CGoban 1.9.12
pattern = Pattern.compile("(\\d{1,2}):(\\d{2})");
matcher = pattern.matcher(value);
if (matcher.matches())
{
assert matcher.groupCount() == 2;
return (Integer.parseInt(matcher.group(1)) * 60000L
+ Integer.parseInt(matcher.group(2)) * 1000L);
}
pattern = Pattern.compile("(\\d+):(\\d{2}):(\\d{2})");
matcher = pattern.matcher(value);
if (matcher.matches())
{
assert matcher.groupCount() == 3;
return (Integer.parseInt(matcher.group(1)) * 3600000L
+ Integer.parseInt(matcher.group(2)) * 60000L
+ Integer.parseInt(matcher.group(3)) * 1000L);
}
pattern =
Pattern.compile("(\\d+)\\s*(?:h|hr|hrs|hours|hours)(?:\\s+each)?+");
matcher = pattern.matcher(value);
if (matcher.matches())
{
assert matcher.groupCount() == 1;
return Integer.parseInt(matcher.group(1)) * 3600000L;
}
pattern =
Pattern.compile("(\\d+)\\s*(?:m|min)");
matcher = pattern.matcher(value);
if (matcher.matches())
{
assert matcher.groupCount() == 1;
return Integer.parseInt(matcher.group(1)) * 60000L;
}
}
catch (NumberFormatException e2)
{
assert false; // patterns should match only valid integers
}
return -1;
}
/** Make constructor unavailable; class is for namespace only. */
private SgfUtil()
{
}
private static Overtime parseOvertime(String value, String regex,
long timeUnitFactor)
{
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(value);
if (matcher.matches())
{
assert matcher.groupCount() == 1;
try
{
String group = matcher.group(1);
Overtime overtime = new Overtime();
overtime.m_byoyomiMoves = 1;
overtime.m_byoyomi =
(long)(Double.parseDouble(group) * timeUnitFactor);
return overtime;
}
catch (NumberFormatException e)
{
// should not happen if patterns match only integer
assert false;
return null;
}
}
else
return null;
}
private static Overtime parseOvertime(String value, String regex,
boolean byoyomiMovesFirst,
long timeUnitFactor)
{
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(value);
if (matcher.matches())
{
assert matcher.groupCount() == 2;
try
{
String group1;
String group2;
if (byoyomiMovesFirst)
{
group1 = matcher.group(1);
group2 = matcher.group(2);
}
else
{
group1 = matcher.group(2);
group2 = matcher.group(1);
}
Overtime overtime = new Overtime();
overtime.m_byoyomiMoves = Integer.parseInt(group1);
overtime.m_byoyomi =
(long)(Double.parseDouble(group2) * timeUnitFactor);
return overtime;
}
catch (NumberFormatException e)
{
// should not happen if patterns match only integer
assert false;
return null;
}
}
else
return null;
}
}