// (c) 2005 kp@kp73.com
using System;
namespace KP.Tetris{
/// <summary>
/// Manages the state of a tetramino, it's location and orientation.
/// </summary>
public class TetraminoState : ICloneable
{
private Tetramino m_piece;
private Coordinate m_location;
private Direction m_orientation = Direction.North;
/// <summary>
/// Number of times the tetramino in play has dropped.
/// </summary>
private int m_dropCount;
/// <summary>
/// Get the number of times the tetramino in play has dropped.
/// </summary>
public int DropCount
{
get { return m_dropCount; }
}
internal TetraminoState(Tetramino t)
{
m_piece = t;
}
/// <summary>
/// Get the tetramino definition.
/// </summary>
public Tetramino Tetramino
{
get { return m_piece; }
}
/// <summary>
/// Get or set the location of the tetramino.
/// </summary>
public Coordinate Location
{
get { return m_location; }
set { m_location = value; }
}
/// <summary>
/// Get or set the orientation of the tetramino.
/// </summary>
public Direction Orientation
{
get { return m_orientation; }
set { m_orientation = value; }
}
/// <summary>
/// Time of the last downward movement.
/// </summary>
internal long m_lastDropTicks = Environment.TickCount; // System.DateTime.Now.Ticks;
/// <summary>
/// Get time of the last downward movement.
/// </summary>
public long LastDropTicks
{
get { return m_lastDropTicks; }
}
/// <summary>
/// Has the tetramino landed in the play area.
/// </summary>
/// <returns></returns>
internal bool HasLanded(Board playArea)
{
bool hasLanded = false;
// get a map of the piece
TetraminoMap appearance = Tetramino.GetAppearance();
// for each cell in that the piece occupies on the board
int row = 0;
for (int y = BottomBound; y <= TopBound; y++)
{
int col = 0;
for (int x = LeftBound; x <= RightBound; x++)
{
// if this cell is part of the moved piece and
// is occupied on the board or reached the last row
if (appearance[col, row].IsOccupied
&& (y == 0 || playArea[x, y - 1].IsOccupied))
{
hasLanded = true;
}
col++;
if (hasLanded) break;
}
row++;
if (hasLanded) break;
}
return hasLanded;
}
/// <summary>
/// Get the left bound of the tetramino shape. This is
/// the leftmost populated column in the tetramino shape map.
/// </summary>
public int LeftBound
{
get { return Location.X - TetraminoMap.PointOfOrigin.X + 1; }
}
/// <summary>
/// Get the right bound of the tetramino shape. This is
/// the rightmost populated column in the tetramino shape map.
/// </summary>
public int RightBound
{
get { return Location.X + (TetraminoMap.MaxWidth - TetraminoMap.PointOfOrigin.X); }
}
/// <summary>
/// Get the top bound of the tetramino shape. This is
/// the highest populated row in the tetramino shape map.
/// </summary>
public int TopBound
{
get { return Location.Y + TetraminoMap.PointOfOrigin.Y - 1; }
}
/// <summary>
/// Get the bottom bound of the tetramino shape. This is
/// the lowest populated row in the tetramino shape map.
/// </summary>
public int BottomBound
{
get { return Location.Y - (TetraminoMap.MaxWidth - TetraminoMap.PointOfOrigin.Y); }
}
/// <summary>
/// Move the tetramino to the right one cell.
/// </summary>
private void MoveRight()
{
Location.X += 1;
}
/// <summary>
/// Move the tetramino to the left one cell.
/// </summary>
private void MoveLeft()
{
Location.X -= 1;
}
/// <summary>
/// Rotate the tetramino shape anti clockwise.
/// </summary>
private void Rotate()
{
// rotate
Orientation = Orientation + 1;
if (Orientation > this.Tetramino.MaxRotation)
Orientation = Direction.North;
}
/// <summary>
/// Move the tetramino in the play area.
/// </summary>
/// <param name="movement">direction of movement</param>
/// <param name="playArea">play area instance</param>
private void Move(Movement movement, Board playArea)
{
switch (movement)
{
case Movement.Left:
MoveLeft();
break;
case Movement.Right:
MoveRight();
break;
case Movement.Drop:
Drop(playArea);
break;
case Movement.Rotate:
Rotate();
break;
}
}
/// <summary>
/// Determine if the requested movement is valid
/// in the conetext of the current play area.
/// </summary>
/// <param name="movement">Direction of movement</param>
/// <param name="playArea">play area instance</param>
/// <returns></returns>
private bool IsValidMovement(Movement movement, Board playArea)
{
bool isValidMovement = true;
// get the state that the piece would be in if the move were applied
TetraminoState movedState = Clone() as TetraminoState;
movedState.Move(movement, playArea);
// get the map of the moved pieces appearance
TetraminoMap movedAppearance = movedState.Tetramino.GetAppearance(movedState);
int row = 0;
// for each cell that the moved shape would occupy on the board
for (int y = movedState.BottomBound; y <= movedState.TopBound; y++)
{
int col = 0;
for (int x = movedState.LeftBound; x <= movedState.RightBound; x++)
{
// if the cell is part of the moved shape
if (movedAppearance[col, row].IsOccupied)
{
// if cell is off the board
if (x < 0 || y < 0 || x >= Board.Width) // || y >= Board.Height)
isValidMovement = false;
else
if (y >= Board.Height) isValidMovement = true;
else
if (playArea[x, y].IsOccupied) isValidMovement = false;
}
col++;
if (!isValidMovement) break;
}
row++;
if (!isValidMovement) break;
}
return isValidMovement;
}
/// <summary>
/// Drop the tetramino to the lowest
/// possible poistion on the play area.
/// </summary>
internal void Drop(Board playArea)
{
while (!this.HasLanded(playArea))
//this.GameInstance.DropOneRow(true);
this.DropOneRow(true);
}
/// <summary>
/// Drop the active piece own row down the play area.
/// </summary>
/// <param name="userDrop"></param>
internal void DropOneRow(bool userDrop)
{
if (!userDrop) m_dropCount += 1;
this.Location.Y -= 1;
// remember when last drop occurred
m_lastDropTicks = Environment.TickCount; // System.DateTime.Now.Ticks;
}
/// <summary>
/// Move the tetramino in the play area, only if the
/// requested movement is valid in the context of
/// the current play area.
/// </summary>
/// <param name="movement">direction of movement</param>
/// <param name="playArea">play area instance</param>
/// <returns></returns>
internal bool RequestMove(Movement movement, Board playArea)
{
bool isValidMovement = movement == Movement.Drop || IsValidMovement(movement, playArea);
if (isValidMovement) Move(movement, playArea);
return isValidMovement;
}
#region ICloneable Members
/// <summary>
/// Get a duplicate instance.
/// </summary>
/// <returns></returns>
public object Clone()
{
TetraminoState clone = new TetraminoState(this.Tetramino);
clone.Location = new Coordinate(this.Location.X, this.Location.Y);
clone.Orientation = this.Orientation;
return clone;
}
#endregion
}
}
|