Page 2 of 2

Re: Random "Pippen Fuseki" Generator

Posted: Mon Mar 02, 2015 6:48 pm
by moyoaji
Pippen wrote:Hopefully your little tool makes my breakthrough to a new level (maybe I'll get some easy resigns because people feel bothered^^). Also I'd be interested in the code since I think it's not too difficult so I could learn a little bit (but only if its convenient with you). Did you also have pseudo-code before programming or did you write the whole thing out of the cold hand? In any way: Big thank you. You invested time to write a code, just to help someone and make him happier. That's special, thx bro.
You're welcome Pippen.

I wrote this fairly quickly, so I didn't write out pseudo-code or plan out a structure beforehand. I had already written the logic for a go board once before; I think every go playing programmer does that at some point. ;-) Actually, the documentation of the class for actually generating the moves is just your description from that fuseki thread over a year ago. The rest was making a go board, which I had partially done before.

The program is very much object-oriented. In general terms, here's how it works:

The application begins and brings up the first window. When the user hits Star Game, the program reads and interprets the information they entered. Using that, it brings up the second window and begins the game. If the user wants black to play a random move, then it generates one and plays it on the board. The graphical go board is by far the most complex part of the program. However, once I taught the program how to interpret user input from the board, it just comes down to the move generator class.

I commented the class fairly well (which I thank myself for because it made coming back to it after a year a lot easier) so I'll put the code here. If you want to see how it works, you can take a look. The main method for move generation is "public Point getNextMove()" so I'll put that in first and, if you want to take a look at the rest, I'll include it too.

Code: Select all

/**
 * Gets the next move as a new point and adds the new move to the move list
 * 
 * @return the next move
 */
public Point getNextMove() {
	Point move = NO_MOVE;
	boolean tooClose = true;
	long tries = 0L;
	long tooMany = 31415926L; // You got a better number in mind?

	// While the move is too close for comfort, keep getting a new one until
	// we've tried too many times. Then there probably isn't a legal move...
	while (tooClose && tries < tooMany) {
		tries++;
		tooClose = false;

		// Determines where on the line the move is (between lines 3 and 17)
		int coor1 = rand.nextInt(15) + 3;
		int coor2 = rand.nextInt(15) + 3;

		// Make sure the move isn't too high or low on the board
		if (coor1 > maxHeight && coor2 > maxHeight
				&& coor1 < 19 - maxHeight + 1 && coor2 < 19 - maxHeight + 1) {
			tooClose = true; // Mark it as too close because that's effectively the same
		}

		// Mark the move as a point on the board
		move = new Point(coor1, coor2);

		// Check if the move is too close to one of our own stones
		for (int x = 0; x < moves.size() && !tooClose; x++) {
			if ((Math.abs((moves.get(x).x - move.x))) <= ownDistance
					&& (Math.abs((moves.get(x).y - move.y))) <= ownDistance) {
				tooClose = true;
			}
		}

		// Check if the move is too close to an opponent's stone
		for (int y = 0; y < enemyMoves.size() && !tooClose; y++) {
			if ((Math.abs(enemyMoves.get(y).x - move.x)) <= enemyDistance
					&& (Math.abs(enemyMoves.get(y).y - move.y)) <= enemyDistance) {
				tooClose = true;
			}
		}
	}

	// If we tried too many times, return a non-move instead.
	if (tries >= tooMany) {
		System.out.println("You're playing too many moves!");
		return NO_MOVE;
	}

	// If we made it this far, play the move and return the point
	moves.add(move);
	return new Point(move.x, move.y);
}
Here's the full code for that class:

Code: Select all

package com.lifein19x19.pippenFuseki.engine;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;

/**
 * Generates a "Pippen Fuseki" based on these parameters
 * 
 * 1. Mark every intersection on the 3rd and 4th line with a number (=104
 * numbers).
 * 
 * 2. Get a random generator.
 * 
 * 3. Play the first move in the corner on x-x (my choice: 4-4).
 * 
 * 4. Then let the random generator decide the next moves under the following
 * conditions:
 * 
 * a) If a numver comes up that is already occupied by a stone then let a new
 * number be created.
 * 
 * b) If a number comes up that is (vertically or horizonally) as near as y (my
 * choice: 1) intersections on one of your own stones then let a new number be
 * created.
 * 
 * c) Stop this algorithm after your move y (my choice: 7) or if you have to
 * create a 5th number on a move.
 * 
 * 5. Continue the game according to what was dealt to you.
 * 
 * @author moyoaji
 */
public class Generator {

	/** Signifies a non-move */
	public static final Point NO_MOVE = new Point(-1, -1);

	/** The minimum distance between two of your own moves */
	private int ownDistance;

	/**
	 * The minimum distance between one of your moves and one of your opponent's
	 * moves
	 */
	private int enemyDistance;

	/** The RNG */
	private Random rand = new Random();

	/** The list of moves */
	private ArrayList<Point> moves;

	/** The list of opponent's moves */
	private ArrayList<Point> enemyMoves;

	/** The highest point on the board that a random move can be */
	private int maxHeight;

	/**
	 * Creates a generator based on parameters
	 * 
	 * @param firstMove black's first move
	 * @param secondMove white's first move
	 * @param ownDistance how far apart you want moves to be from your own
	 *            stones
	 * @param enemyDistance how far you want moves to be from opponent's stones
	 */
	public Generator(Point firstMove, Point secondMove, int ownDistance,
			int enemyDistance, int maxHeight) {
		moves = new ArrayList<Point>();
		enemyMoves = new ArrayList<Point>();

		this.ownDistance = ownDistance;
		this.enemyDistance = enemyDistance;
		this.maxHeight = maxHeight;

		// Add the first and second moves if they were given, otherwise don't
		if (!firstMove.equals(NO_MOVE)) {
			moves.add(firstMove);
		}
		if (!secondMove.equals(NO_MOVE)) {
			enemyMoves.add(secondMove);
		}
	}

	/**
	 * Inputs a move the opponent made
	 * 
	 * @param move the move the opponent just made
	 */
	public void inputMove(Point move) {
		if (move != NO_MOVE) {
			enemyMoves.add(move);
		}
	}

	/**
	 * Gets the next move as a new point and adds the new move to the move list
	 * 
	 * @return the next move
	 */
	public Point getNextMove() {
		Point move = NO_MOVE;
		boolean tooClose = true;
		long tries = 0L;
		long tooMany = 31415926L; // You got a better number in mind?

		// While the move is too close for comfort, keep getting a new one until
		// we've tried too many times. Then there probably isn't a legal move...
		while (tooClose && tries < tooMany) {
			tries++;
			tooClose = false;

			// Determines where on the line the move is (between lines 3 and 17)
			int coor1 = rand.nextInt(15) + 3;
			int coor2 = rand.nextInt(15) + 3;

			// Make sure the move isn't too high or low on the board
			if (coor1 > maxHeight && coor2 > maxHeight
					&& coor1 < 19 - maxHeight + 1 && coor2 < 19 - maxHeight + 1) {
				tooClose = true; // Mark it as too close because that's
									// effectively the same
			}

			// Mark the move as a point on the board
			move = new Point(coor1, coor2);

			// Check if the move is too close to one of our own stones
			for (int x = 0; x < moves.size() && !tooClose; x++) {
				if ((Math.abs((moves.get(x).x - move.x))) <= ownDistance
						&& (Math.abs((moves.get(x).y - move.y))) <= ownDistance) {
					tooClose = true;
				}
			}

			// Check if the move is too close to an opponent's stone
			for (int y = 0; y < enemyMoves.size() && !tooClose; y++) {
				if ((Math.abs(enemyMoves.get(y).x - move.x)) <= enemyDistance
						&& (Math.abs(enemyMoves.get(y).y - move.y)) <= enemyDistance) {
					tooClose = true;
				}
			}
		}

		// If we tried too many times, return a non-move instead.
		if (tries >= tooMany) {
			System.out.println("You're playing too many moves!");
			return NO_MOVE;
		}

		moves.add(move);

		return new Point(move.x, move.y);
	}

	/**
	 * Gets the moves generated so far as a new ArrayList
	 * 
	 * @return the moves generated so far
	 */
	public ArrayList<Point> getMoves() {
		ArrayList<Point> theMoves = new ArrayList<Point>(moves.size());
		for (int i = 0; i < moves.size(); i++) {
			theMoves.add(moves.get(i));
		}
		return theMoves;
	}

	/**
	 * Gets the moves your opponent played so far as a new ArrayList
	 * 
	 * @return the moves played so far
	 */
	public ArrayList<Point> getOppMoves() {
		ArrayList<Point> theMoves = new ArrayList<Point>(moves.size());
		for (int i = 0; i < enemyMoves.size(); i++) {
			theMoves.add(enemyMoves.get(i));
		}
		return theMoves;
	}

	/**
	 * Generates the list of moves
	 */
	public String generateList() {
		String list = "In this game I played the rare ";

		for (int i = 0; i < moves.size(); i++) {
			list = list + moves.get(i).x + "-" + moves.get(i).y;
			if (i < moves.size() - 1) {
				list = list + ", ";
			}
		}

		list = list + " opening";

		return list;
	}
}
EDIT: And I took the time to whip up an icon for this.