Skip to content

Instantly share code, notes, and snippets.

@bhugueney
Created January 24, 2017 13:37
Show Gist options
  • Save bhugueney/261fb2c3d81d401e220bf6f45abb0ac5 to your computer and use it in GitHub Desktop.
Save bhugueney/261fb2c3d81d401e220bf6f45abb0ac5 to your computer and use it in GitHub Desktop.
minimal Goban
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
class Group{
final boolean color;
final int[] stones;
Set<Integer> freedoms;
public Group(int pos, boolean color, int[] freedoms){
this.color= color;
stones= new int[]{pos};
this.freedoms = new HashSet<Integer>();
for(int p : freedoms){
this.freedoms.add(p);
}
}
public Group(Group g0, Group g1, int connection){
color= g0.color;// g1.color must obviously be the same
stones= new int[g0.stones.length + g1.stones.length];
System.arraycopy(g0.stones, 0, stones, 0, g0.stones.length);
System.arraycopy(g1.stones, 0, stones, g0.stones.length, g1.stones.length);
freedoms= new HashSet<Integer>(g0.freedoms);
freedoms.addAll(g1.freedoms);
freedoms.remove(connection);
}
public void removeFreedom(int p){
freedoms.remove(p);
}
public void addFreedom(int p){
freedoms.add(p);
}
boolean getColor(){
return color;
}
int[] getStones(){
return stones;
}
public boolean isAtari(){
return freedoms.size()==1;
}
public boolean isDead(){
return freedoms.isEmpty();
}
}
public class Goban{
int size;
Group[] board;
int[] scores= new int []{0,0};
int[][] neighbors;
static int[] deltas={-1,1};
private static boolean isValid(int r, int c, int size){
return (r>=0) && (c >=0) && (r < size) && (c < size);
}
private static int[] fromList(List<Integer> al){
int[] res= new int[al.size()];
for(int i=0; i != res.length; ++i){
res[i]= al.get(i);
}
return res;
}
public Goban(int size){
this.size= size;
board= new Group[size*size];
neighbors= new int[board.length][];
for(int r= 0, p=0; r != size; ++r){
for(int c=0; c != size; ++c, ++p){
List<Integer> currentN=new ArrayList<Integer>();
for(int d : deltas){
int rd= r+d;
int cd= c+d;
if(isValid(rd, c, size)){
currentN.add(pos(rd,c));
}
if(isValid(r, cd, size)){
currentN.add(pos(r,cd));
}
}
neighbors[p]= fromList(currentN);
}
}
}
private int pos(int r, int c){
return r*size+c;
}
private int[] getNeighbors(int pos){
return neighbors[pos];
}
private int[] freedoms(int pos){
ArrayList<Integer> res= new ArrayList<Integer>();
for(int np : neighbors[pos]){
if(board[np]==null){
res.add(np);
}
}
return fromList(res);
}
private void empty(int pos){
board[pos]= null;
for(int np : neighbors[pos]){
if(board[np] != null){
board[np].addFreedom(pos);
}
}
}
private void attacking(int pos, boolean color){
for(int np : neighbors[pos]){
if((board[np] != null) &&(board[np].getColor() != color)){
if(board[np].isAtari()){
int[] toRemove= board[np].getStones();
for(int p : toRemove){
empty(p);
}
scores[color?1:0]+= toRemove.length;
}else{
board[np].removeFreedom(pos);
}
}
}
}
private Group grouping(int pos, boolean color){
Group res= new Group(pos, color, freedoms(pos));
for(int np : neighbors[pos]){
if((board[np] != null) && (board[np].getColor() == color)){
res = new Group(board[np], res, pos);
}
}
return res;
}
private boolean play(int pos, boolean color){
if(board[pos] != null){
return false;
}
attacking(pos, color);
Group newGroup= grouping(pos, color);
if(newGroup.isDead()){
empty(pos);
return false;
}else{
for(int p : newGroup.getStones()){
board[p]= newGroup;
}
}
return true;
}
public boolean play(int r, int c, boolean color){
return isValid(r,c, size) && play(pos(r,c), color);
}
public boolean isEmpty(int r, int c){
return board[pos(r, c)]==null;
}
public boolean getColor(int r, int c){
return board[pos(r,c)].getColor();
}
public int getScore(boolean c){
return scores[c?1:0];
}
}
public class GoGUI {
final static int SIZE= 8;
final static double EMPTY_RADIUS=0.1;
final static double STONE_RADIUS=0.45;
final static String PASS_STRING= "PASS";
final static int PASS_X=0;
public static void main(String args[]){
StdDraw.setXscale(-1, SIZE);
StdDraw.setYscale(-1, SIZE);
Goban goban= new Goban(SIZE);
boolean currentPlayer= false;
for(int consecutivePass= 0; consecutivePass != 2; currentPlayer= !currentPlayer){
display(goban, currentPlayer);
boolean legalMove=false;
while(!legalMove){
double[] xy= waitForClick();
if(isPass(xy[0], xy[1])){
++consecutivePass;
legalMove= true;
}else{
int[] rc= toIntersection(xy);
if(rc != null){
legalMove=goban.play(rc[0], rc[1], currentPlayer);
if(legalMove){
consecutivePass= 0;
}
}
}
}
}
StdDraw.setPenColor(StdDraw.LIGHT_GRAY);
StdDraw.filledRectangle(SIZE/2., -1., SIZE/2.,0.75);
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.text(SIZE/2., -1, "Game over");
displayScores(goban);
}
private static void displayScores(Goban g){
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.textLeft(-1,-1,""+g.getScore(true));
StdDraw.setPenColor(StdDraw.WHITE);
StdDraw.textLeft(0,-1,""+g.getScore(false));
}
private static void display(Goban g, boolean currentPlayer){
StdDraw.clear(StdDraw.LIGHT_GRAY);
StdDraw.setPenColor(StdDraw.BLACK);
for(int rc=0; rc!= SIZE; ++rc){
StdDraw.line(0, rc, SIZE-1, rc);
StdDraw.line(rc, 0, rc, SIZE-1);
}
for(int r=0; r != SIZE; ++r){
for(int c=0; c != SIZE; ++c){
StdDraw.setPenColor((g.isEmpty(r,c) || g.getColor(r,c)) ? StdDraw.BLACK : StdDraw.WHITE);
StdDraw.filledCircle(r,c, g.isEmpty(r,c) ? EMPTY_RADIUS : STONE_RADIUS) ;
}
}
displayScores(g);
StdDraw.setPenColor(currentPlayer? StdDraw.BLACK : StdDraw.WHITE);
StdDraw.text(SIZE/2., -1, "Now Playing");
StdDraw.text(SIZE-1, -1, "PASS");
}
private static double[] waitForClick(){
while(!StdDraw.mousePressed()){
}
while(StdDraw.mousePressed()){
}
return new double[]{StdDraw.mouseX(), StdDraw.mouseY()};
}
private static boolean isPass(double x, double y){
double dx= Math.round(x)-(SIZE-1);
double dy= Math.round(y)+1.;
return (dx*dx+dy*dy)<1.;
}
private static int[] toIntersection(double[] xy){
double roundedX= Math.round(xy[0]);
double roundedY= Math.round(xy[1]);
double dX= xy[0] - roundedX;
double dY= xy[1] - roundedY;
return ((dX*dX + dY*dY)<STONE_RADIUS) ? new int[]{(int)roundedX, (int)roundedY} : null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment