All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opensudoku.go.Core Maven / Gradle / Ivy

Go to download

Computer Go engine using GTP, Go Text Protocol please refer to http://en.wikipedia.org/wiki/GNU_Go

The newest version!
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.opensudoku.go;

import com.opensudoku.go.exception.GoBadNotValidStoneException;
import com.opensudoku.go.exception.GoBadNoGroupForEmptyException;
import com.opensudoku.go.exception.GoBadNotOnBoardException;
import com.opensudoku.go.exception.GoBadViolateKORuleException;
import com.opensudoku.go.exception.GoBadViolateSuicideRuleException;
import com.opensudoku.go.exception.GoBadException;
import com.opensudoku.go.exception.GoBadViolateNonEmptyRuleException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Standard 19x19 board with basic Go game operation 1.play: system can
 * automatically remove dead stones check 4 directions capturing if no any
 * capture but itself is dead, throw GoBadViolateSuicideRuleException
 * 2.removeStone MAYBE TO REVERSE, NEVER ALLOW IT??? 3.any illegal move, system
 * performs designed Exception GoBadNotOnBoardException for any given point is
 * not on the board coordinate GoBadNotValidStoneException for any given stone
 * is not BLACK or WHITE GoBadViolateNonEmptyRuleException
 * GoBadViolateSuicideRuleException GoBadViolateKORuleException
 *
 * @author mark
 */
public final class Core implements Coordinate {
    private String comment="";
    private int[] go;
    private int ko; // the location of KO, if no KO, then it's NO_KO. NO_KO=-1
    private int tempGroupColor;
    private int[] tempGroupArray = new int[361];
    private int tempGroupCounter = 0;

    private Group group;
    private Group coat; // group's coat
    private Group liberty; // group's coat

    private HashSet tempGroup;
    private ArrayList mapT19;

    private int lastCapturingPoint;

    public boolean isKo() {
        if (ko == NO_KO) {
            return false;
        }
        return true;
    }

    /**
     *
     * @return 0 to 360, indicates KO's location, or NO_KO
     */
    public int getKo() {
        return ko;
    }

    /**
     *
     * @param ko
     * @throws GoBadException
     */
    public void setKo(int ko) throws GoBadException {
        if (ko != NO_KO) {
            if (ko < 0 || ko > 360) {
                throw new GoBadException("wrong KO location ");
            }

        }
        this.ko = ko;
    }

    public String getComment() {
        return comment;
    }
    

    //TODO...
    public boolean isKoViolation(int id) {
        if (getKo() == id) {
            return true;
        }
        return false;
    }

    //TODO...
    public boolean isEmpty(int id) {
        if (this.go[id] == EMPTY) {
            return true;
        }
        return false;
    }

    public boolean isLegal(int color, String q16) throws GoBadNotOnBoardException, GoBadException {
        String str = q16.toUpperCase();
        int id = mapT19.indexOf(str);
        if (id == -1) {
            throw new GoBadNotOnBoardException(" val is " + str);
        }

        return isLegal(color, id);
    }

    public boolean isEye(int color, int id) throws GoBadException {
        List surrounding = new ArrayList<>();

        if (COL_NUM[id] <= 17) { // when not yet meet RIGHT BORDER
            if (!(go[id + 1] == color)) {// right stone must be the same color
                return false;
            }
            surrounding.add(id + 1);
        }
        // try LEFT
        if (COL_NUM[id] >= 1) { // when not yet meet RIGHT LEFT
            if (!(go[id - 1] == color)) {// previous id
                return false;
            }

            surrounding.add(id - 1);
        }
// try DOWN 
        if (ROW_NUM[id] <= 17) { // 
            if (!(go[id + 19] == color)) {// 
                return false;
            }

            surrounding.add(id + 19);
        }
        // try UP
        if (ROW_NUM[id] >= 1) { // 
            if (!(go[id - 19] == color)) {// 
                return false;
            }
            surrounding.add(id - 19);
        }

        //
        // only when all surrounding are in one group, it's eye
        //    otherwise, not eye yet (for advacne viewpoint, it can be eye!)
        Core core2 = new Core();
        core2 = this.copy();
        core2.go[id] = EMPTY;
        //core2.play(ko, color, id);
        int firstSurround = surrounding.get(0);
        Group grp = core2.getGroup(firstSurround);

        for (int k = 1; k < surrounding.size(); k++) {
            if (!grp.isMember(surrounding.get(k))) {
                return false;
            }
        }
        return true;

    }

    public boolean isLegal(int color, int id) throws GoBadException {
        if (!isEmpty(id)) {
            return false;
        }
        if (isSuicide(color, id)) {
            return false;
        }

        if (isKoViolation(id)) {
            return false;
        }

        return true;
    }

    //TODO...
    public boolean isSuicide(int color, int id) {
        Core core2 = new Core();
        core2 = this.copy();
        try {
            core2.play(id, color);
//

        } catch (GoBadViolateSuicideRuleException ex) {
            return true;
        } catch (GoBadException ex) {
            return false;
        }
        return false;
    }

    public int getStone(int id) {
        return go[id];
    }

    public int getStone(int row, int col) {
        return go[row * 19 + col];
    }

    public int[] getGo() {
        return go;
    }

    public void setGo(int[] go) {
        this.go = go;
    }

    public void play(String colrow, int stone) throws GoBadException {
        int id = mapT19.indexOf(colrow.toUpperCase());
        if (id == -1) {
            throw new GoBadNotOnBoardException();
        }

        play(id, stone);

    }

    /**
     * Basic operation to put stone (BLACK or WHITE) on board and perform
     * capture
     *
     * @param id 0 to 360
     * @param stonecolor BLACK or WHITE
     * @throws GoBadException
     */
    public void play(int id, int stonecolor) throws GoBadException {

        if (id < 0 || id > 360) {
            throw new GoBadNotOnBoardException();
        }

// must set stone on emppty point
        if (go[id] != EMPTY) {
            throw new GoBadViolateNonEmptyRuleException("id=" + id + " stonecolor=" + stonecolor);
        }

        // stone must be BLACK or WHITE
        if ((stonecolor != BLACK) && (stonecolor != WHITE)) {
            throw new GoBadNotValidStoneException();
        }

        // good habit to use getVal at left to avoid assign something to it
        if (getKo() == id) {
            throw new GoBadViolateKORuleException();
        }

        go[id] = stonecolor;
        int captureCnt = capture(id);
        if (captureCnt > 0) {
//            show("%%%%%%%%%%%%%%%%%%%%%%%%%%capture:" + captureCnt);
        } else { // check suicise
            if (isSuicide(id)) {

                go[id] = EMPTY; // ver 0.5, for suicide case, need to undo

                throw new GoBadViolateSuicideRuleException(" id=" + id);
            }

        }

        //
        // === KO section ===
        //
        ko = NO_KO; // after one legal move, reset KO.

        // only if capturing 1 opponenet stone and itself is atari
        //   set KO
        if (captureCnt == 1) {
            if (isAtari(id)) {
                ko = lastCapturingPoint;
            }
        }
        
        //
        //
//        if (isEye(stonecolor, id)){
//            comment=" BLIND EYE!!!";
//        }else{
//            comment=" ...";
//        }

    }

    /**
     * This is opponent's group. When its liberty is 0, it is being captured!
     *
     * @param id
     * @return
     */
    private int captureOpponent(int id) throws GoBadNoGroupForEmptyException, GoBadException {

        int cnt = 0;

        getGroup(id);
        getCoat(group.getList());
        getLiberty(coat.getList());
//        System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$xxx ??? " + liberty.size());

        if (liberty.size() == 0) {
//            System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$xxx ??? " + " removing...");

            for (int k = 0; k < group.size(); k++) {
                lastCapturingPoint = group.get(k);
                this.removeStone(group.get(k));
//                System.out.println("xxx removing " + group.get(k));
            }
            return group.size();
        }

        return 0;
    }

    /**
     * for any given point, to check if its liberty is 1 only
     *
     * @param id
     * @return
     * @throws GoBadNoGroupForEmptyException
     * @throws GoBadException
     */
    private boolean isAtari(int id) throws GoBadNoGroupForEmptyException, GoBadException {

        getGroup(id);
        getCoat(group.getList());
        getLiberty(coat.getList());
//        System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$xxx ??? " + liberty.size());

        if (liberty.size() == 1) {
            return true;
        }

        return false;
    }

    private boolean isSuicide(int id) throws GoBadNoGroupForEmptyException, GoBadException {

        int cnt = 0;

        getGroup(id);
        getCoat(group.getList());
        getLiberty(coat.getList());
//        System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$xxx ??? " + liberty.size());

        if (liberty.size() == 0) {
            return true;
        }

        return false;
    }

    /**
     * Given point is to capture opponents in 4 directions
     *
     * @param id
     */
    private int capture(int id) throws GoBadException {
        int cnt = 0;
        int color = getStone(id);

        if (COL_NUM[id] <= 17) { // when not yet meet RIGHT BORDER
            if (go[id + 1] != color && go[id + 1] != EMPTY) {// next id
                cnt += captureOpponent(id + 1);
            }
        }
        // try LEFT
        if (COL_NUM[id] >= 1) { // when not yet meet RIGHT LEFT
            if (go[id - 1] != color && go[id - 1] != EMPTY) {// previous id
                cnt += captureOpponent(id - 1);
            }
        }
// try DOWN 
        if (ROW_NUM[id] <= 17) { // 
            if (go[id + 19] != color && go[id + 19] != EMPTY) {// 
                cnt += captureOpponent(id + 19);
            }
        }
        // try UP
        if (ROW_NUM[id] >= 1) { // 
            if (go[id - 19] != color && go[id - 19] != EMPTY) {// 
                cnt += captureOpponent(id - 19);
            }
        }

        return cnt;
    }

    /**
     * Returns given group's liberty
     *
     * @param id
     * @return
     */
    public Group getLiberty(List list) throws GoBadException {
        int cnt = 0;

        if (list.size() == 0) {
            throw new GoBadException("EMPTY GROUP CANNOT HAVE liberty count!");
        }
        int color = getStone(list.get(0));
        liberty = new Group(color); //TODO

        for (Integer id : list) {
            // when not yet meet RIGHT BORDER
            if (go[id] == EMPTY) {// next id

                liberty.add(id);
            }
        }

        return liberty;
    }

    /**
     * Return given group's member list
     *
     * @param id
     * @return
     */
//    public Set getGroup(int id) throws GoBadNoGroupForEmptyException {
//        if (go[id] == EMPTY) {
//            throw new GoBadNoGroupForEmptyException();
//        }
//
//        tempGroup = new HashSet();
//        tempGroup.add(id);
//        tempGroupColor = getStone(id);
//        int cnt = 0;
//        do {
//            cnt = tempGroup.size();
//            show("======= cnt:" + cnt);
//            formGroup();
//
//        } while (tempGroup.size() > cnt);
//
//        return tempGroup;
//        //  return null;
//    }
    public Group getCoat(List list) throws GoBadException {
        if (list.size() == 0) {
            throw new GoBadException("EMPTY GROUP CANNOT HAVE COAT");
        }
        int color = getStone(list.get(0));
        coat = new Group(color); //TODO

        for (Integer id : list) {

            if (COL_NUM[id] <= 17) { // when not yet meet RIGHT BORDER
                if (go[id + 1] != color) {// next id
                    coat.add(id + 1);
                }
            }
            // try LEFT
            if (COL_NUM[id] >= 1) { // when not yet meet RIGHT LEFT
                if (go[id - 1] != color) {// previous id
                    coat.add(id - 1);
                }
            }
// try DOWN 
            if (ROW_NUM[id] <= 17) { // 
                if (go[id + 19] != color) {// 
                    coat.add(id + 19);
                }
            }
            // try UP
            if (ROW_NUM[id] >= 1) { // 
                if (go[id - 19] != color) {// 
                    coat.add(id - 19);
                }
            }

        }

        return coat;

    }

    public Group getGroup(int id) throws GoBadNoGroupForEmptyException {
        if (go[id] == EMPTY) {
            throw new GoBadNoGroupForEmptyException();
        }
        group = new Group(getStone(id));
        group.add(id);

        int cnt = 0;
        do {
            cnt = group.size();
            formGroup();

        } while (group.size() > cnt);

        return group;
        //  return null;
    }

    public static void show(String str) {
        System.out.println(str);
    }

    private void formGroup() {
        int id = 0;
        int color = group.getColor();
        for (int k = 0; k < group.size(); k++) {
            id = group.get(k);
            // try RIGHT 
            if (COL_NUM[id] <= 17) { // when not yet meet RIGHT BORDER
                if (go[id + 1] == color) {// next id
                    group.add(id + 1);
                }
            }
            // try LEFT
            if (COL_NUM[id] >= 1) { // when not yet meet RIGHT LEFT
                if (go[id - 1] == color) {// previous id
                    group.add(id - 1);
                }
            }
// try DOWN 
            if (ROW_NUM[id] <= 17) { // 
                if (go[id + 19] == color) {// 
                    group.add(id + 19);
                }
            }
            // try UP
            if (ROW_NUM[id] >= 1) { // 
                if (go[id - 19] == color) {// 
                    group.add(id - 19);
                }
            }

            // try DOWN 
//            if (ROW_NUM[s] <= 16) { // when not yet meet RIGHT BORDER
//                show("debug formGroup:" + s + " ROW NUM:" + ROW_NUM[s]);
//
//                if (go[s + 19] == tempGroupColor) {// next 19 id
//                    show(" ... debug fromGroup:" + (s + 19) + " ROW NUM:" + ROW_NUM[s + 19]);
//
//                    tempGroup.add(s + 19);
//                show(" ......" +tempGroup.size());
//    
//                }
//            }
            // try UP 
//            if (ROW_NUM[s] >=1) { // when not yet meet RIGHT BORDER
//                if (go[s-19] == tempGroupColor) {// next 19 id
//                    tempGroup.add(s-19);
//                }
//            }
        }
    }

    public void removeStone(int id) throws GoBadException {
        if (id < 0 || id > 360) {
            throw new GoBadNotOnBoardException();
        }
        // must set stone on emppty point
        if (go[id] == EMPTY) {
            throw new GoBadException("Removing not existing stone");
        }

        go[id] = EMPTY;
    }

    /**
     * Sets stone to given point(row,col), call play in order to and performs
     * capturing in one method
     *
     * @param row
     * @param col
     * @param val
     * @throws GoBadException
     */
    public void play(int row, int col, int val) throws GoBadException {
        play(row * 19 + col, val);

        // go[row * 19 + col] = val;
    }

//    public Core() throws GoBadException {
    public Core() {
        init();
        mapT19 = new ArrayList<>();
        for (int k = 0; k < 361; k++) {
            mapT19.add(Coordinate.T19[k]);
        }

    }

    public void init() {
//        setKo(NO_KO);
        ko = NO_KO;
        go = new int[361];
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int k = 0; k < 361; k++) {
            sb.append(go[k]);
        }
        return sb.toString();
    }

    public void show() {

        StringBuilder sb = new StringBuilder();
        int id = 0;
        sb.append("\n    ");

        for (int n = 0; n < 19; n++) {
            sb.append(COL_NAME[n]).append(" ");
        }
        sb.append("\n");

        for (int m = 0; m < 19; m++) {
            sb.append(" ").append(ROW_NAME[m]);
            for (int n = 0; n < 19; n++) {
                id = m * 19 + n;
                switch (go[id]) {
                    case EMPTY:
                        if (isStar(m, n)) {
                            sb.append(" +");

                        } else {
                            sb.append(" .");
                        }
                        break;
                    case BLACK:
                        sb.append(" ").append(BLACK_CHAR);
                        break;
                    case WHITE:
                        sb.append(" ").append(WHITE_CHAR);
                        break;
                }
            }
            sb.append(" ");
            sb.append(ROW_NAME[m]);
            sb.append("\n");

        }
        sb.append("   ");
        for (int n = 0; n < 19; n++) {
            sb.append(" ").append(COL_NAME[n]);
        }
        System.out.println(sb.toString());
    }

    private boolean isStar(int m, int n) {
        if (m == 3 || m == 9 || m == 15) {
            if (n == 3 || n == 9 || n == 15) {
                return true;
            }

        }

        return false;
    }

    public Core copy() {
        Core x = new Core();
        x.go = go.clone();

        return x; //To change body of generated methods, choose Tools | Templates.
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy