PICHU.
using System;using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Flood_Control
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Creates a texture of Tile_Sheet.png, Background.png, and TitleScreen.png
Texture2D playingPieces, backgroundScreen, titleScreen;
GameBoard gameBoard;
Vector2 gameBoardDisplayOrigin = new Vector2(70, 89); //Vector Point where the board will be drawn
int playerScore = 0;
enum GameStates { Titlescreen, Playing };
GameStates gamestate = GameStates.Titlescreen;
Rectangle EmptyPiece = new Rectangle(1, 247, 40, 40);
const float MinTimeSinceLastinput = 0.25f;
float timeSinceLastInput = 0.0f;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
this.IsMouseVisible = true;
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 600;
graphics.ApplyChanges();
gameBoard = new GameBoard();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
playingPieces = Content.Load<Texture2D>(@"Textures\Tile_Sheet");
backgroundScreen = Content.Load<Texture2D>(@"Textures\Background");
titleScreen = Content.Load<Texture2D>(@"Textures\TitleScreen");
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
switch (gamestate)
{
case GameStates.Titlescreen:
if (Keyboard.GetState().IsKeyDown(Keys.Space))
{
gameBoard.ClearBoard();
gameBoard.GenerateNewPieces(false);
playerScore = 0;
gamestate = GameStates.Playing;
}break;
case GameStates.Playing:
timeSinceLastInput += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (timeSinceLastInput >= MinTimeSinceLastinput)
{
HandleMouseInput(Mouse.GetState());
}
gameBoard.ResetWater();
for (int y = 0; y < GameBoard.GameBoardHeight; y++)
{
CheckScoringChain(gameBoard.GetWaterChain(y));
}
gameBoard.GenerateNewPieces(true);
break;
}
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
if (gamestate == GameStates.Titlescreen)
{
spriteBatch.Begin();
//Draws the titlescreen
spriteBatch.Draw(titleScreen, new Rectangle(0, 0, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height), Color.White);
spriteBatch.End();
}
if (gamestate == GameStates.Playing)
{
spriteBatch.Begin();
spriteBatch.Draw(backgroundScreen, new Rectangle(0, 0, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height), Color.White);
for (int x = 0; x < GameBoard.GameBoardWidth; x++)
{
for (int y = 0; y < GameBoard.GameBoardHeight; y++)
{
int pixelX = (int)gameBoardDisplayOrigin.X + (x * GamePiece.PieceWidth);
int pixelY = (int)gameBoardDisplayOrigin.Y + (y * GamePiece.PieceHeight);
spriteBatch.Draw(playingPieces, new Rectangle(pixelX, pixelY, GamePiece.PieceWidth, GamePiece.PieceHeight), EmptyPiece, Color.White);
spriteBatch.Draw(playingPieces, new Rectangle(pixelX, pixelY, GamePiece.PieceWidth, GamePiece.PieceHeight), gameBoard.GetSourceRect(x, y), Color.White);
}
}
this.Window.Title = playerScore.ToString();
spriteBatch.End();
}
base.Draw(gameTime);
}
private int DetermineScore(int SquareCount)
{
return (int)((Math.Pow((SquareCount / 5), 2) + SquareCount) * 10);
}
private void CheckScoringChain(List<Vector2> WaterChain)
{
if (WaterChain.Count > 0)
{
Vector2 LastPipe = WaterChain[WaterChain.Count - 1];
if (LastPipe.X == GameBoard.GameBoardWidth - 1)
{
if (gameBoard.hasConnector((int)LastPipe.X, (int)LastPipe.Y, "Right"))
{
playerScore += DetermineScore(WaterChain.Count);
foreach (Vector2 ScoringSquare in WaterChain)
{
gameBoard.SetSquare((int)ScoringSquare.X, (int)ScoringSquare.Y, "Empty"); ;
}
}
}
}
}
private void HandleMouseInput(MouseState mouseState)
{
int x = ((mouseState.X - (int)gameBoardDisplayOrigin.X) / GamePiece.PieceWidth);
int y = ((mouseState.Y - (int)gameBoardDisplayOrigin.Y) / GamePiece.PieceHeight);
if ((x >= 0) && (x < GameBoard.GameBoardWidth) && (y >= 0) && (y < GameBoard.GameBoardHeight))
{
if (mouseState.LeftButton == ButtonState.Pressed)
{
gameBoard.RotatePiece(x, y, false);
timeSinceLastInput = 0.0f;
}
if (mouseState.RightButton == ButtonState.Pressed)
{
gameBoard.RotatePiece(x, y, true);
timeSinceLastInput = 0.0f;
}
}
}
}
}
using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Flood_Control
{
class GamePiece
{
//Names each of the different types of game pieces that will be added to the game board
public static string[] PieceTypes = {
"Left,Right", "Top,Bottom", "Left,Top", "Top,Right", "Right,Bottom", "Bottom,Left", "Empty"
};
//Declares the Height and the Width of the sprite
public const int PieceHeight = 40;
public const int PieceWidth = 40;
//Index of the last piece that can be placed on the board
public const int MaxPlayablePiecesIndex = 4;
//Declares the position of an empty piece
public const int EmptyPieceIndex = 6;
//Describes the layout of the texture file.
//Offsets pixels left and top edge of the texture and adds a single pixel of padding betweene ach sprite on the sprite sheet
private const int textureOffsetX = 1;
private const int textureOffsetY = 1;
private const int texturePaddingX = 1;
private const int texturePaddingY = 1;
//Stores the associated values of a piece
private string pieceType = "";
private string pieceSuffix = "";
public string PieceType { get { return pieceType; } }
public string Suffix { get { return pieceSuffix; } }
public GamePiece(string type, string suffix)
{
pieceType = type;
pieceSuffix = suffix;
}
public GamePiece(string type)
{
pieceType = type;
pieceSuffix = "";
}
//An overload, Can Pass both type and piece
public void SetPiece(string type, string suffix)
{
pieceType = type;
pieceSuffix = suffix;
}
//An overload, Can Pass the type
public void SetPiece(string type)
{
SetPiece(type, "");
}
//Checks to see if the piece is already contains the suffix
//If it does, nothing happens.
//If not, the suffix value is passed to the method and added to the pieceSuffix member variable
public void AddSuffix(string suffix)
{
if (!pieceSuffix.Contains(suffix)) { pieceSuffix += suffix; }
}
//Uses the Replace method of the string class to remove the passed suffix from the pieceSuffix variable
public void RemoveSuffix(string suffix)
{
pieceSuffix = pieceSuffix.Replace(suffix, "");
}
public void RotatePiece(bool Clockwise)
{
switch (pieceType)
{
case "Left,Right":
pieceType = "Top,Bottom";
break;
case "Top,Bottom":
pieceType = "Left,Right";
break;
case "Left,Top":
if (Clockwise)
pieceType = "Top,Right";
else
pieceType = "Bottom,Left";
break;
case "Top,Right":
if (Clockwise)
pieceType = "Right,Bottom";
else
pieceType = "Left,Top";
break;
case "Right,Bottom":
if (Clockwise)
pieceType = "Bottom,Left";
else
pieceType = "Top,Right";
break;
case "Bottom,Left":
if (Clockwise)
pieceType = "Left,Top";
else
pieceType = "Right,Bottom";
break;
case "Empty":
break;
}
}
//Creates an empty List object for holding the ends we want to return to the calling code
//Uses the split() method of the string class to get each end listed in the pieceType
//EX Top,Bottom will return two elements, Top and Bottom
public string[] GetOtherEnds(string startingEnd)
{
List<string> opposites = new List<string>();
foreach (string end in pieceType.Split(','
)
{
if (end != startingEnd) { opposites.Add(end); }
}
return opposites.ToArray();
}
//Returns 'true' if the pieceType string contains the string value passed in as the direction parameter
//This allowed the GamePiece class to determine if the piece has a connector facing in particular direction
public bool HasConnector(string direction)
{
return pieceType.Contains(direction);
}
public Rectangle GetSourceRect()
{
int x = textureOffsetX;
int y = textureOffsetY;
if (pieceSuffix.Contains("W"))
{
x += PieceWidth + texturePaddingX;
}
y += (Array.IndexOf(PieceTypes, pieceType) * (PieceHeight + texturePaddingY));
return new Rectangle(x, y, PieceWidth, PieceHeight);
}
}
}
using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
namespace Flood_Control
{
class GameBoard
{
//Generate Random Numbers
Random rand = new Random();
//Size of the playing board
public const int GameBoardWidth = 8;
public const int GameBoardHeight = 10;
//Arrays provide the storage mechanism for the GamePiece objects that make up the 8 by 10 piece board
private GamePiece[,] boardSquares = new GamePiece[GameBoardWidth, GameBoardHeight];
//Identify scoring pipe combinations.
private List<Vector2> WaterTracker = new List<Vector2>();
public GameBoard()
{
ClearBoard();
}
public void ClearBoard()
{
for (int x = 0; x < GameBoardWidth; x++)
{
for (int y = 0; y < GameBoardHeight; y++)
{
boardSquares[x, y] = new GamePiece("Empty");
}
}
}
//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public void RotatePiece(int x, int y, bool clockwise)
{
boardSquares[x, y].RotatePiece(clockwise);
}
//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public Rectangle GetSourceRect(int x, int y)
{
return boardSquares[x, y].GetSourceRect();
}
//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public string GetSquare(int x, int y)
{
return boardSquares[x, y].PieceType;
}
//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public void SetSquare(int x, int y, string pieceName)
{
boardSquares[x, y].SetPiece(pieceName);
}
//Locates the appropriate GamePiece within the boardSquares array and passes on the function request to the piece
public bool hasConnector(int x, int y, string direction)
{
return boardSquares[x, y].HasConnector(direction);
}
//Gets a random value from the PieceTypes array and assigns it to a GamePiece.
//With the Rnadom.NExt() method overload used here, the second parameter is non-inclusive
//In order to generate a number from 0 to 5, the second parameter needs to be 6
public void RandomPiece(int x, int y)
{
boardSquares[x, y].SetPiece(GamePiece.PieceTypes[rand.Next(0, GamePiece.MaxPlayablePiecesIndex+1)]);
}
//Looks at the piece above to see if it makred as Empty. If it is, the method will subtract one from rowLookup and start over until
//it reaches the top of the board. If no non-empty pieces are found when the top of the board is reached, the method does nothign and exits.
//When a non-empty piece is found, it is copies to the destination square
//The copy is changed to an empty piece. The rowLookup variable is set to -1 to ensure that the loop does not continue to run
public void FillFromAbove(int x, int y)
{
int rowLookUp = y - 1;
while (rowLookUp >= 0)
{
if (GetSquare(x, rowLookUp) != "Empty")
{
SetSquare(x, y, GetSquare(x, rowLookUp));
SetSquare(x, rowLookUp, "Empty");
rowLookUp = -1;
}
rowLookUp--;
}
}
//When called with 'true' passed as dropSquares, the loping logic processes one column at a time from the bottom up.
//When it finds and empty square it called FillFromAbove() to pull a filled square from above into that locaiton.
//The Reason the processing order is imporant here is that, by filling a lower square from a higher position,
//That higher position will become empty. It in turn will need to be filled from above
//After the holes are filled or dropSquares is set to false, GenerateNewPieces() examines each square in BoardSquares and asks it to generate a random piece for each square
//that contains an empty piece
public void GenerateNewPieces(bool dropSquares)
{
if (dropSquares)
{
for (int x = 0; x < GameBoard.GameBoardWidth; x++)
{
for (int y = GameBoard.GameBoardHeight - 1; y >= 0; y--)
{
if (GetSquare(x, y) == "Empty")
{
FillFromAbove(x, y);
}
}
}
for (int y = 0; y < GameBoard.GameBoardHeight; y++)
{
for (int x = 0; x < GameBoard.GameBoardWidth; x++)
{
if (GetSquare(x, y) == "Empty")
{
RandomPiece(x, y);
}
}
}
}
}
//Loops through each item in the boardSquares array and removes the W suffix from the GamePiece.
//To fill a piece with water, the FillePiece() method adds the W suffix to the GamePiece
//Recall that by having a W suffix, the GetSourceRect() method of the GamePiece shifts the source rectangle one tile to the right on the sprite sheet,
//returning the image for a pipe filled with water instead of an empty pipe
public void ResetWater()
{
for (int y = 0; y < GameBoardHeight; y++)
{
for (int x = 0; x < GameBoardWidth; x++)
{
boardSquares[x, y].RemoveSuffix("W");
}
}
}
public void FillPiece(int X, int Y)
{
boardSquares[X, Y].AddSuffix("W");
}
public void PropagateWater(int x, int y, string fromDirection)
{
if ((y >= 0) && (y < GameBoardHeight) && (x >= 0) && (x < GameBoardWidth))
{
if (boardSquares[x, y].HasConnector(fromDirection) && !boardSquares[x, y].Suffix.Contains("W"))
{
FillPiece(x, y);
WaterTracker.Add(new Vector2(x, y));
foreach (string end in boardSquares[x, y].GetOtherEnds(fromDirection))
{
switch (end)
{
case "Left": PropagateWater(x - 1, y, "Right"); break;
case "Right": PropagateWater(x + 1, y, "Left"); break;
case "Top": PropagateWater(x, y - 1, "Bottom"); break;
case "Bottom": PropagateWater(x, y + 1, "Top"); break;
}
}
}
}
}
public List<Vector2> GetWaterChain(int y)
{
WaterTracker.Clear();
PropagateWater(0, y, "Left");
return WaterTracker;
}
}
}
PICHU.
Copyright © 2026, NextGenUpdate.
All Rights Reserved.