📄 board.cs
字号:
////////////////////////////////////////////////
//
// Project: Lines.NET
// Version: 1.1
// Author: Vladimir L.
//
// homepage: http://www.boomsoft.org
// e-mail: support@boomsoft.org
//
// Copyright (c) 2003-2004, Boomsoft.org
//
using System;
using System.Drawing;
namespace Lines.Core
{
/// <summary>
/// Represents game board the field where differently colored balls are placed and moved.
/// </summary>
/// <remarks>
/// A single instance of this class is incapsulated and used by <see cref="Game"/> class. There is no need
/// to create other instances of this class. Each time the new game is started a new instance of this class
/// is created.
/// </remarks>
public class Board
{
/// <summary>
/// Refers to the owner, an instance of <see cref="Game"/> class.
/// </summary>
private Game game;
/// <summary>
/// Defines the field size of game board (width, height).
/// </summary>
private Size size;
/// <summary>
/// Defines the maximum index of color that might be generated during game.
/// </summary>
private int colors;
/// <summary>
/// A 2-D array of balls, which is the game field itself.
/// </summary>
private Ball[,] board;
/// <summary>
/// Reference to pseudo-random number generator that is used to allocate new balls.
/// </summary>
private Random random;
/// <summary>
/// Gets the game board field size (width, height).
/// </summary>
public Size BoardSize
{
get {return size;}
}
/// <summary>
/// Gets the maximum index of color that might be generated during game.
/// </summary>
/// <remarks>
/// The colors of balls will vary between 1 and value of this property inclusively.
/// </remarks>
public int Colors
{
get {return colors;}
}
#region Constructors
/// <summary>
/// Creates an instance of Board class.
/// <seealso cref="AppSettings.BoardWidth"/>
/// <seealso cref="AppSettings.BoardHeight"/>
/// <seealso cref="AppSettings.Colors"/>
/// </summary>
/// <param name="game">The reference to board owner, an instance of <see cref="Game"/> class.</param>
/// <remarks>
/// Creates an instance of Board class with default size and number of colors defined by
/// <see cref="AppSettings"/> class.
/// </remarks>
public Board(Game game)
: this(game, new Size(AppSettings.Instance.BoardWidth, AppSettings.Instance.BoardHeight))
{
}
/// <summary>
/// Creates an instance of Board class.
/// <seealso cref="AppSettings.Colors"/>
/// </summary>
/// <param name="game">The reference to board owner, an instance of <see cref="Game"/> class.</param>
/// <param name="size">The game board field size (width, height).</param>
/// <remarks>
/// Creates an instance of Board class with default number of colors defined by
/// <see cref="AppSettings"/> class.
/// </remarks>
public Board(Game game, Size size)
: this(game, size, AppSettings.Instance.Colors)
{
}
/// <summary>
/// Creates an instance of Board class.
/// </summary>
/// <param name="game">The reference to board owner, an instance of <see cref="Game"/> class.</param>
/// <param name="size">The game board field size (width, height).</param>
/// <param name="colors">The number ball colors that are used during the game.</param>
public Board(Game game, Size size, int colors)
{
this.game = game;
this.size = size;
this.board = new Ball[size.Width, size.Height];
this.colors = colors;
this.random = new Random((int)DateTime.Now.Ticks);
}
#endregion
/// <summary>
/// Gets ball at a certain location on the board.
/// </summary>
/// <param name="x">The x-coordinate of location.</param>
/// <param name="y">The y-coordinate of location.</param>
/// <returns>A reference to a <see cref="Ball"/> object that is located on specified position,
/// or <c>null</c> if this position is empty.</returns>
public Ball GetBallAt(int x, int y)
{
return board[x, y];
}
/// <summary>
/// Creates an instance of <see cref="Ball"/> class with predefined color and places it
/// on a certain posion on the board.
/// </summary>
/// <param name="x">The x-coordinate of location the ball to be placed on.</param>
/// <param name="y">The y-coordinate of location the ball to be placed on.</param>
/// <param name="color">The color index of ball.</param>
/// <returns>The reference to a newly created ball.</returns>
public Ball CreateBallAt(int x, int y, int color)
{
Ball ball = new Ball(this, color, x, y);
SetBallAt(ball, x, y);
return ball;
}
/// <summary>
/// Sets ball at a certain position on the board.
/// </summary>
/// <param name="ball">The ball to be set.</param>
/// <param name="x">The x-coordinate of ball's new location.</param>
/// <param name="y">The y-coordinate of ball's new location.</param>
/// <exception cref="ModelException">If the position the ball should be set to is already occupied.</exception>
public void SetBallAt(Ball ball, int x, int y)
{
if (GetBallAt(x, y) != null)
{
throw new ModelException("The position [" + x + ", " + y + "] is already occupied!");
}
board[x, y] = ball;
ball.MoveTo(x, y);
}
/// <summary>
/// Removes ball from a certain position on the board.
/// </summary>
/// <param name="x">The x-coordinate of ball's location.</param>
/// <param name="y">The y-coordinate of ball's location.</param>
public void DeleteBallAt(int x, int y)
{
board[x, y] = null;
}
/// <summary>
/// Computes the amount of empty squares on the board.
/// </summary>
/// <returns>The amount of empty locations.</returns>
public int GetEmptySquaresCount()
{
int result = 0;
for (int i = 0; i < size.Width; i++)
{
for (int j = 0; j < size.Height; j++)
{
if (GetBallAt(i, j) == null)
{
result++;
}
}
}
return result;
}
/// <summary>
/// Randomly selects a one of the empty squares on the board.
/// </summary>
/// <returns>The randomly selected empty location.</returns>
public Point GetRandomEmptySquare()
{
int pos = random.Next(1, GetEmptySquaresCount() + 1);
int curPos = 0;
for (int i = 0; i < size.Width; i++)
{
for (int j = 0; j < size.Height; j++)
{
if (GetBallAt(i, j) == null)
{
curPos++;
if (curPos == pos)
{
return new Point(i, j);
}
}
}
}
throw new ModelException("Enable to allocate a new empty position!");
}
/// <summary>
/// Generates random balls for the next step.
/// </summary>
/// <param name="amount">The amount of balls to be created.</param>
/// <returns>An array of generated balls, or <c>null</c> if there is no free place to allocate
/// such amount of balls.</returns>
/// <remarks>
/// The locations and colors of balls are randomly selected with a help of
/// <see cref="GetRandomEmptySquare()"/> and <see cref="GetRandomColor()"/> methods.
/// </remarks>
public Ball[] GenerateVirtualBalls(int amount)
{
if (GetEmptySquaresCount() < amount)
{
//throw new ModelException("Unable to allocate " + amount + " empty positions!");
return null;
}
Ball[] result = new Ball[amount];
for (int i = 0; i < amount; i++)
{
Point pos = GetRandomEmptySquare();
result[i] = CreateBallAt(pos.X, pos.Y, GetRandomColor());
}
for (int i = 0; i < amount; i++)
{
DeleteBallAt(result[i].X, result[i].Y);
}
return result;
}
/// <summary>
/// Generates random color index.
/// </summary>
/// <returns>The random index color.</returns>
/// <remarks>
/// Tha range of colors lies between 1 and <see cref="Colors"/> inclusively.
/// </remarks>
public int GetRandomColor()
{
return random.Next(1, colors + 1);
}
/// <summary>
/// Gets the ball that is currently selected on the board.
/// </summary>
/// <returns>The reference to a ball that is currently selected, or <c>null</c> in
/// case there is no selected ball on the board.</returns>
public Ball GetSelectedBall()
{
for (int i = 0; i < size.Width; i++)
{
for (int j = 0; j < size.Height; j++)
{
Ball ball = GetBallAt(i, j);
if ((ball != null) &&
(ball.Selected))
{
return ball;
}
}
}
return null;
}
/// <summary>
/// Sets selected status to a certain ball.
/// <seealso cref="DeselectBall()"/>
/// </summary>
/// <param name="ball">The ball to be selected.</param>
/// <remarks>
/// Previously selected ball will be automatically diselected.
/// </remarks>
public void SelectBall(Ball ball)
{
SelectBall(ball.X, ball.Y);
}
/// <summary>
/// Sets selected status to a ball on a certain location.
/// <seealso cref="DeselectBall()"/>
/// </summary>
/// <param name="x">The x-coordinate of location.</param>
/// <param name="y">The y-coordinate of location.</param>
/// <remarks>
/// Previously selected ball will be automatically diselected. If there is no ball
/// under specified location there will not be any selected ball by the end of this
/// operation.
/// </remarks>
public void SelectBall(int x, int y)
{
// Deselect a ball if one is already selected
DeselectBall();
// Select a new ball
Ball ball = GetBallAt(x, y);
if (ball != null)
{
ball.Select(true);
}
}
/// <summary>
/// Diselects the currently selected ball if any.
/// </summary>
public void DeselectBall()
{
Ball ball = GetSelectedBall();
if (ball != null)
{
ball.Select(false);
}
}
/// <summary>
/// Creates route between two points with a help of <see cref="PathFinder"/> class.
/// </summary>
/// <param name="from">The point to build the route from.</param>
/// <param name="to">The destination point of route.</param>
/// <returns>An array of points to move between defined locations if the <see cref="PathFinder"/>
/// was able to find a route, <c>null</c> otherwise.</returns>
public Point[] CreateRoute(Point from, Point to)
{
int[,] matrix = new int[BoardSize.Width, BoardSize.Height];
for (int i = 0; i < BoardSize.Width; i++)
{
for (int j = 0; j < BoardSize.Height; j++)
{
Ball ball = GetBallAt(i, j);
if (ball != null)
{
matrix[i, j] = PathFinder.POS_OCCUPIED;
}
}
}
PathFinder pathFinder = new PathFinder(matrix);
return pathFinder.FindPath(from, to);
}
/// <summary>
/// Moves a ball from one position to another.
/// </summary>
/// <param name="from">The location where to move the ball from.</param>
/// <param name="to">The destination location of the ball.</param>
/// <exception cref="ModelException">If there is no ball under the <c>from</c> location or
/// the <c>to</c> location is currently occupied.</exception>
public void MoveBall(Point from, Point to)
{
Ball ball = GetBallAt(from.X, from.Y);
if (ball == null)
{
throw new ModelException("There is no ball under position [" + from.X + ", " + from.Y + "]");
}
Ball posTo = GetBallAt(to.X, to.Y);
if (posTo != null)
{
throw new ModelException("The position [" + to.X + ", " + to.Y + "] is already occupied!");
}
board[from.X, from.Y] = null;
board[to.X, to.Y] = ball;
ball.MoveTo(to.X, to.Y);
}
/// <summary>
/// Finds balls that compose lines to remove them from the board.
/// </summary>
/// <param name="ballsInLine">Defines the minimum length of line (See <see cref="Game.BallsInLine"/>).</param>
/// <returns>An array of balls that should be removed from the board at the current step, or
/// <c>null</c> if there is no such.</returns>
public Ball[] FindBallsToDestroy(int ballsInLine)
{
int[,] matrix = new int[BoardSize.Width, BoardSize.Height];
for (int x = 0; x < BoardSize.Width; x++)
{
for (int y = 0; y < BoardSize.Height; y++)
{
Ball ball = GetBallAt(x, y);
matrix[x, y] = ball != null ? ball.Color : -1;
}
}
DestroyFinder finder = new DestroyFinder(matrix, ballsInLine);
Point[] locations = finder.FindLocations();
Ball[] result = new Ball[locations.Length];
int i = 0;
foreach (Point location in locations)
{
result[i++] = GetBallAt(location.X, location.Y);
}
return result;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -