简体   繁体   中英

Refactor C# chess game to follow MVC design pattern

I've been given a university assignment whereby I have to develop a chess game in C# WinForms and it has been recommended that we follow the Model-View-Controller design pattern. I'm struggling to identify what code should go where, so I'm hoping for a bit of advice and a general critique of my design. For those brave enough to attempt traversing my spaghetti code, here's the full project . For everyone else, here's a description of what I've come up with so far:

Model layer:

  • Piece - Represents a single chess piece. Has derived classes King , Queen , Rook , etc. that override virtual methods to define movement behaviour for each piece type.
  • Player - A small utility class that doesn't do much besides storing each player's pieces in a List<Piece> object and performing global operations on them that a single piece can't (or shouldn't) perform on its own, such as testing for check, clearing en passant and castle moves, etc.

View layer:

  • frmChess - The main form of the application. Overrides WndProc to keep the form's 1:1 aspect ratio when the user resizes it, but is otherwise just a normal form.
  • Board - A simple derivation of a standard WinForms TableLayoutPanel that I've edited in the Visual Studio designer to have 8 columns and rows. A single instance of this class sits on the main form and really doesn't do anything besides simplifying the alignment and presentation of the squares.

Controller layer:

  • Game - A static class that stores the 64 Square s (see below) in a List<Square> object, and has various methods that allow getting the square at a given (x, y) coordinate, saving/loading the game state to/from a binary file and detecting checkmate.

Unsure :

  • Square - Another simple derivation of a standard WinForms control, namely Panel . 64 of these have been added to the Board at design time and each one is either empty, or holds a single Piece instance at any given time. That piece also holds a circular reference to its square, which I'd like to change since it seems like squares belong in the view layer, and the model should know nothing of the view. I've also overriden the square's OnPaint method to draw its contained piece's image on its foreground, and have an event handler in the main form hooked up to every square's Click event so that pieces can be selected and moved. I can think of lots of other things to do from here as well, such as determining other squares that can be attacked from, or are attacking, this square. So that makes me think that maybe it should be part of the model after all...any thoughts?

  • PieceMove - This class handles the doing and undoing of moves when testing for check, and also allows testing whether a move will cause check for the moving piece's team, making that move invalid. I really don't know whether this should be considered as part of the model, or a move controller. Also on that note, should I have a separate move controller, view controller, etc. or just one controller class that does all of that?

Lastly, I know this post is getting way too long so I apologise for that, but I'd like to implement an event-driven method of notifying the view of the model's changes, eg select a piece, move a piece, team is in check, team is in checkmate, etc. How would I go about that?

Thank you all in advance for taking the time to read this and/or my code; I really appreciate it! :D

I think you're off to a good start there, with all the core concepts clearly defined.

I'd break the model down slightly differently:

Model

  • Board - Contains the current state of pieces on the board.
    • Properties: [List<PieceState> Pieces]
    • Methods: void PlacePiece(Piece, Square) , void MovePiece(FromSquare, ToSquare) , void RemovePiece(Square)
  • Piece : [Type] , [Colour]
  • Square - Represents a square on the board.
    • Properties: [Coordinates]
  • PieceState : [Piece], [IsActive] , [Square]
  • MoveValidator - Ensures that a piece can perform the desired move.
    • Methods: bool MoveIsValid(Piece, FromSquare, ToSquare)
  • GameState - Describes the overall state of the game.
    • Values: InPlay , CheckMate , StaleMate , etc.
  • Player : Represents a player.
    • Properties: [Colour] , [List<Piece> Pieces]
  • GameLogic - Would leverage MoveValidator , as well as containing logic to ensure that pieces remain within the board boundaries, and that a square contained only one piece. This class would also contain logic to determine the current GameState .
    • Properties: Board
    • Has: MoveValidator
    • Methods: void SetupBoard(Board) , bool CheckMoveIsValid(Piece, FromSquare, ToSquare) , GameState GetGameState()

View

This is the part of the program that handles rendering.

Think of your model as containing all the logic and state for the game, and the view layer as the logic that "paints" it on the screen.

In terms of any design pattern, the technical implementation is not overly important. What is important is to keep things loosely coupled , and ensure that things are grouped into the right areas.

The main functions of the view are:

  1. Render the board.
  2. Render the pieces on the board with the correct icon and colour.
  3. Show relevant game statistics, score, active pieces, game time, etc.

Controller

The controller provides the plumbing between the model, view, and user interaction. It "glues" your classes together.

The controller needs to leverage and orchestrate the game classes. The responsibilities are:

  1. Set up a new board, then pass it back to the view for rendering.
  2. Receive user input, validate it, then update the board accordingly. Pass the update view back for rendering.
  3. Stop play if a player wins, or a stale-mate is reached (as determined by GameState ).

This is far from exhaustive, but it should provide some food for thought.

Take a look here for more info on MVC structure .

Other patterns you may wish to look at are MVVM (model view view-model) , and MVP (model view presenter) . In my opinion MVP is the best choice for a WinForms app.

Lastly, there is no right way to do this! Follow good design practice (but don't be dogmatic about it), keep things loosely coupled , and you should end up with spaghetti-free code .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM