/** * Join a game room by player's name (username) * @param string Username * @return a valid (non-null) player object */ public NetworkBackgammonPlayer Enter(string _playerName, string pw) { NetworkBackgammonPlayer newPlayer = new NetworkBackgammonPlayer(_playerName); // Validate player bool goodPlayer = gamePlayerList.VerifyLogin(_playerName, pw); // Check the user is already in the list if (!connectedPlayers.Contains(newPlayer) && goodPlayer) { // Add new player object to the game room list connectedPlayers.Add(newPlayer); // Start listening to this player newPlayer.AddListener(this); // Have the player start listening to this game room AddListener(newPlayer); // Broadcast the player connected event to all registered listeners Broadcast(new NetworkBackgammonGameRoomEvent(NetworkBackgammonGameRoomEvent.GameRoomEventType.PlayerConnected)); } else { newPlayer = null; } return(newPlayer); }
private void buttonConnect_Click(object sender, EventArgs e) { if (player == null) { if (textBoxPlayerName.Text.Trim() != "") { if (gameRoom != null) { string playerName = textBoxPlayerName.Text.Trim(); // Register (if not already) gameRoom.RegisterPlayer(playerName, "password"); // Enter the game room player = gameRoom.Enter(playerName, "password"); if (player != null) { buttonConnect.Text = "Disconnect"; textBoxPlayerName.Enabled = false; groupBoxGameRoomControls.Enabled = true; player.AddListener(this); UpdateConnectedPlayersList(); } else { MessageBox.Show("Login failed!", "Login Error (username)!", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } else { MessageBox.Show("Login failed (no game room connection)!", "Login Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } else { MessageBox.Show("Login failed (no username specified)!", "Login Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } else { if (gameRoom != null) { gameRoom.Leave(player); gameRoom.RemoveListener(this); player.RemoveListener(this); player = null; } buttonConnect.Text = "Connect"; textBoxPlayerName.Enabled = true; groupBoxGameRoomControls.Enabled = false; } }
// Show the game board display private void ShowBoard(bool show) { if (show) { m_backgammonBoard.TopLevel = false; // Set the Parent Form of the Child window. m_backgammonBoard.Parent = this; m_backgammonBoard.Top = SystemInformation.CaptionHeight; // Display the new form. m_backgammonBoard.Show(); m_backGammonChat.TopLevel = false; // Set the Parent Form of the Child window. m_backGammonChat.Parent = this; m_backGammonChat.Left = m_backgammonBoard.Left; m_backGammonChat.Top = m_backgammonBoard.Bottom; m_backGammonChat.Width = this.Width - 10; // Display the new form. m_backGammonChat.Show(); m_backGammonScoreBoardPlayer1.TopLevel = false; // Set the Parent Form of the Child window. m_backGammonScoreBoardPlayer1.Parent = this; m_backGammonScoreBoardPlayer1.Left = m_backgammonBoard.Right; m_backGammonScoreBoardPlayer1.Top = m_backgammonBoard.Top; m_backGammonScoreBoardPlayer1.Width = 137; m_backGammonScoreBoardPlayer1.Height = m_backgammonBoard.Height / 2; // Set the title for the scoreboard m_backGammonScoreBoardPlayer1.Title = (NetworkBackgammonClient.Instance.Player != null ? NetworkBackgammonClient.Instance.Player.PlayerName : "?"); // Display the new form. m_backGammonScoreBoardPlayer1.Show(); m_backGammonScoreBoardPlayer2.TopLevel = false; // Set the Parent Form of the Child window. m_backGammonScoreBoardPlayer2.Parent = this; m_backGammonScoreBoardPlayer2.Left = m_backgammonBoard.Right; m_backGammonScoreBoardPlayer2.Top = m_backGammonScoreBoardPlayer1.Bottom; m_backGammonScoreBoardPlayer2.Width = 137; m_backGammonScoreBoardPlayer2.Height = m_backgammonBoard.Height / 2; // Get the game session this player is associated with NetworkBackgammonPlayer oppPlayer = NetworkBackgammonClient.Instance.GameRoom.GetOpposingPlayer(NetworkBackgammonClient.Instance.Player); // Set the title for the scoreboard m_backGammonScoreBoardPlayer2.Title = (oppPlayer != null ? oppPlayer.PlayerName : "?"); // Display the new form. m_backGammonScoreBoardPlayer2.Show(); } else { m_backgammonBoard.Hide(); m_backGammonChat.Hide(); m_backGammonScoreBoardPlayer1.Hide(); m_backGammonScoreBoardPlayer2.Hide(); } }
/// <summary> /// Executes a valid move of the active player. /// </summary> /// <param name="player1">Player 1</param> /// <param name="player2">Player 2</param> /// <param name="_checker">Selected checker to be moved</param> /// <param name="_move">Selected move (according to one of the dice values)</param> /// <returns>Success (true) if the active player has won the game, otherwise false</returns> public static bool ExecuteMove(ref NetworkBackgammonPlayer player1, ref NetworkBackgammonPlayer player2, NetworkBackgammonChecker _checker, NetworkBackgammonDice _move) { // Determine the active player NetworkBackgammonPlayer activePlayer = player1.Active ? player1 : player2; NetworkBackgammonPlayer waitingPlayer = player1.Active ? player2 : player1; bool bActivePlayerWon = true; // Find active player's checker that has been selected to move foreach (NetworkBackgammonChecker checkerAP in activePlayer.Checkers) { // Found checker, now find checker's move that has been selected if (checkerAP == _checker) { foreach (NetworkBackgammonDice move in checkerAP.PossibleMoves) { // Found move if (move == _move) { checkerAP.MoveChecker(_move); if (checkerAP.CurrentPosition.CurrentPosition >= NetworkBackgammonPosition.GameBoardPosition.NORMAL_START && checkerAP.CurrentPosition.CurrentPosition <= NetworkBackgammonPosition.GameBoardPosition.NORMAL_END) { // Check whether active player kicked checker of the waiting player onto the bar foreach (NetworkBackgammonChecker checkerWP in waitingPlayer.Checkers) { if (checkerWP.CurrentPosition.GetOppositePosition().CurrentPosition == checkerAP.CurrentPosition.CurrentPosition) { checkerWP.CurrentPosition.Reset(); } } } break; } } break; } } // Check whether active player has won the game foreach (NetworkBackgammonChecker checker in activePlayer.Checkers) { if (checker.CurrentPosition.CurrentPosition != NetworkBackgammonPosition.GameBoardPosition.OFFBOARD) { bActivePlayerWon = false; break; } } return(bActivePlayerWon); }
public void TestMethod_VerifyInitialPossibleMoves() { // Configure players player1.Active = true; player2.Active = false; // Set reference to active player NetworkBackgammonPlayer activePlayer = player1.Active ? player1 : player2; // Loop through all possible dice combinations foreach (NetworkBackgammonDice.DiceValue diceValueOne in Enum.GetValues(typeof(NetworkBackgammonDice.DiceValue))) { if (diceValueOne >= NetworkBackgammonDice.DiceValue.MIN && diceValueOne <= NetworkBackgammonDice.DiceValue.MAX) { foreach (NetworkBackgammonDice.DiceValue diceValueTwo in Enum.GetValues(typeof(NetworkBackgammonDice.DiceValue))) { if (diceValueTwo >= NetworkBackgammonDice.DiceValue.MIN && diceValueTwo <= NetworkBackgammonDice.DiceValue.MAX) { // Set (not random) dice values dice[0].CurrentValue = diceValueOne; dice[1].CurrentValue = diceValueTwo; bool activePlayerHasPossibleMoves = NetworkBackgammonGameEngine.CalculatePossibleMoves( ref player1, ref player2, dice); Assert.IsTrue(activePlayerHasPossibleMoves, "Active player should always have possible moves after the winning the initial dice roll"); Stream stream = GetManifestResourceStreamByName("InitialCheckers_" + (dice[0].CurrentValueUInt32 <= dice[1].CurrentValueUInt32 ? dice[0].CurrentValueUInt32.ToString() : dice[1].CurrentValueUInt32.ToString()) + "_" + (dice[1].CurrentValueUInt32 >= dice[0].CurrentValueUInt32 ? dice[1].CurrentValueUInt32.ToString() : dice[0].CurrentValueUInt32.ToString()) + ".xml"); Assert.IsNotNull(stream, "Data (XML) for verification comparison purposes is missing"); List <NetworkBackgammonChecker> checkersVerified = LoadCheckersFromXMLFile(stream); string checkerVerificationMessage = ""; bool checkerVerificationResult = VerifyCheckersAndPossibleMoves(activePlayer.Checkers, checkersVerified, ref checkerVerificationMessage); Assert.IsTrue(checkerVerificationResult, "Possible moves of active player for initial dice roll of " + dice[0] + ", " + dice[1] + " incorrect. Detail: " + checkerVerificationMessage); } } } } }
/// <summary> /// Get a players opponent /// </summary> /// <param name="player">Backgammon player to be checked</param> /// <returns>null if player does not exist</returns> public NetworkBackgammonPlayer GetOpponent(NetworkBackgammonPlayer player) { NetworkBackgammonPlayer opp = null; if (ContainsPlayer(player)) { opp = (player == player1 ? player2 : player1); } return(opp); }
// Send a message to the listeners of Player public void SendMsg(string msg) { if (IsConnected) { NetworkBackgammonPlayer recpPlayer = GameRoom.GetOpposingPlayer(Player); if (recpPlayer != null) { Player.Broadcast(new NetworkBackgammonChatEvent(Player.PlayerName, recpPlayer.PlayerName, msg)); } } }
/// <summary> /// Get the player's opponent /// </summary> /// <param name="player">Backgammon player to be checked</param> /// <returns>null if player is not part of a game session</returns> public NetworkBackgammonPlayer GetOpposingPlayer(NetworkBackgammonPlayer player) { NetworkBackgammonPlayer retval = null; // Get the game session associated with this player NetworkBackgammonGameSession gameSession = GetGameSession(player); if (gameSession != null) { retval = gameSession.GetOpponent(player); } return(retval); }
public bool OnConnect(string playerName, string playerPassword, ref string returnMessage) { bool bRetVal = false; if (player == null) { if (gameRoom != null) { // Register (if not already; ignore 'false' return value if already registered) gameRoom.RegisterPlayer(playerName, playerPassword); // Enter the game room player = gameRoom.Enter(playerName, playerPassword); if (player != null) { player.AddListener(this); bRetVal = true; } else { if (returnMessage != null) { returnMessage += "\nPlayer couldn't login with name '" + playerName + "' and password '" + playerPassword + "'."; } } } else { if (returnMessage != null) { returnMessage += "\nNo game room available to connect to!"; } } } else { if (returnMessage != null) { returnMessage += "\nPlayer with name '" + playerName + "' is already connected!"; } bRetVal = false; } return(bRetVal); }
/// <summary> /// Determine whether or not player is participating in a game session /// </summary> /// <param name="player">Backgammon player to be checked</param> /// <returns>null NetworkBackgammonPlayer object if name not found</returns> public NetworkBackgammonPlayer GetPlayerByName(string playerName) { NetworkBackgammonPlayer retval = null; // Look through the list of game sessions and make sure the player is not already playing in another room foreach (NetworkBackgammonPlayer player in connectedPlayers) { if (player.PlayerName.CompareTo(playerName) == 0) { retval = player; break; } } return(retval); }
/// <summary> /// Get the game session the the "player" is currently apart of /// </summary> /// <param name="player">Backgammon player to be checked</param> /// <returns>null if player is not part of a game session</returns> public NetworkBackgammonGameSession GetGameSession(NetworkBackgammonPlayer player) { NetworkBackgammonGameSession retval = null; // Look through the list of game sessions and make sure the player is not already playing in another room foreach (NetworkBackgammonGameSession session in gameSessions) { if (session.ContainsPlayer(player)) { retval = session; break; } } return(retval); }
/// <summary> /// Determine whether or not player is participating in a game session /// </summary> /// <param name="player">Backgammon player to be checked</param> /// <returns>"True" if backgammon player in question is in a game session, otherwise "false"</returns> public bool IsPlayerInGameSession(NetworkBackgammonPlayer player) { bool retval = false; // Look through the list of game sessions and make sure the player is not already playing in another room foreach (NetworkBackgammonGameSession session in gameSessions) { if (session.ContainsPlayer(player)) { retval = true; break; } } return(retval); }
private void challengeToolStripMenuItemChallenge_Click(object sender, EventArgs e) { if (gameRoom != null && player != null && listBoxConnectedPlayers.SelectedItem != null) { NetworkBackgammonPlayer challengedPlayer = (NetworkBackgammonPlayer)listBoxConnectedPlayers.SelectedItem; if (gameRoom.Challenge(player.PlayerName, challengedPlayer.PlayerName)) { groupBoxGameControls.Enabled = true; } else { listBoxLog.Items.Add("Challenge failed (selected opponent rejected or timeout waiting for challenge response)!"); } } }
private void m_playButton_Click(object sender, EventArgs e) { int curIndex = m_gameRoomPlayersListBox.SelectedIndex; if (curIndex >= 0) { NetworkBackgammonPlayer challengingPlayer = NetworkBackgammonClient.Instance.Player; NetworkBackgammonPlayer challengedPlayer = (NetworkBackgammonPlayer)m_gameRoomPlayersListBox.Items[curIndex]; string tempChallengingPlayer = String.Copy(challengingPlayer.PlayerName); string tempChallengedlayer = String.Copy(challengedPlayer.PlayerName); // Setup the wait dialog waitDlg = new NetworkBackgammonWaitDialog(new NetworkBackgammonWaitDialog.WaitButtonActionDelegate(OnChallengeCancelled)); waitDlg.WaitDialogLabel.Text = "Waiting for response from " + challengedPlayer.PlayerName; bool challengeProceed = true; lock (gameChallengeMutex) { // Only execute challenge if none is pending (i.e. if gameChallengeThreadExchangeData object exists) if (gameChallengeThreadExchangeData == null) { // Store challenged player name for thread access gameChallengeThreadExchangeData = new GameChallengeDataContainer(challengedPlayer.PlayerName); } else { challengeProceed = false; } } if (challengeProceed) { // Create the challenge thread challengeThread = new Thread(new ThreadStart(ChallengeThread)); // Start the thread challengeThread.Start(); // Start up the wait dialog waitDlg.ShowDialog(this); } } }
/// <summary> /// Constructor /// </summary> /// <param name="_player1">Network backgammon player 1 to be part of this game session</param> /// <param name="_player2">Network backgammon player 2 to be part of this game session</param> public NetworkBackgammonGameSession(NetworkBackgammonPlayer _player1, NetworkBackgammonPlayer _player2) { defaultNotifier = new NetworkBackgammonNotifier(this); player1 = _player1; player2 = _player2; NetworkBackgammonDice dice1 = new NetworkBackgammonDice(); NetworkBackgammonDice dice2 = new NetworkBackgammonDice(); // Make sure the seeds used for the (pseudo) random generator instances are different // (otherwise, both dice will always roll the same values) while (dice1.Seed == dice2.Seed) { Thread.Sleep(1); dice2 = new NetworkBackgammonDice(); } dice = new NetworkBackgammonDice[] { dice1, dice2 }; }
/// <summary> /// Stops the game session state machine (thread) /// </summary> public void Stop() { if (threadStateMachine != null) { bStateMachineKeepRunning = false; // Wake-up state machine to allow for proper shutdown // (without forcing an abort) semStateMachine.Release(); if (!threadStateMachine.Join(10000)) { threadStateMachine.Abort(); } threadStateMachine = null; } eventQueue.Clear(); // Terminate game for players Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Terminated)); if (player1 != null) { // Game Session stops listening for events from Player 1 player1.RemoveListener(this); // Player 1 stops listening for events from Game Session RemoveListener(player1); } if (player2 != null) { // Game Session stops listening for events form Player 2 player2.RemoveListener(this); // Player 2 stops listening for events from Game Session RemoveListener(player2); } player1 = null; player2 = null; }
/// <summary> /// Exit the game room by player /// </summary> /// <param name="_player">Backgammon player who wants to leave the game room</param> public void Leave(NetworkBackgammonPlayer _player) { if (connectedPlayers.Contains(_player)) { // The disconnected player should no longer be listening... RemoveListener(_player); // Stop listening to this player _player.RemoveListener(this); // Check if player is associated with a game room and remove if necessary NetworkBackgammonGameSession _playerGameSession = GetGameSession(_player); if (_playerGameSession != null) { // Disconnect the player from the game session _player.RemoveListener(_playerGameSession); // Disconnect the game session from the player _playerGameSession.RemoveListener(_player); // Get the opposing player NetworkBackgammonPlayer opposingPlayer = _playerGameSession.GetOpponent(_player); if (opposingPlayer != null) { // Disconnect the player from the game session opposingPlayer.RemoveListener(_playerGameSession); // Disconnect the game session from the player _playerGameSession.RemoveListener(opposingPlayer); } // Halt the game sesssion _playerGameSession.Stop(); } // Remove player from connected player list connectedPlayers.Remove(_player); // Broadcast the player disconnected event to all registered listeners Broadcast(new NetworkBackgammonGameRoomEvent(NetworkBackgammonGameRoomEvent.GameRoomEventType.PlayerDisconnected)); } }
public void OnEventNotification(INetworkBackgammonNotifier sender, INetworkBackgammonEvent e) { if (e is GameSessionCheckerUpdatedEvent) { GameSessionCheckerUpdatedEvent updateEvent = (GameSessionCheckerUpdatedEvent)e; NetworkBackgammonPlayer curPlayer = updateEvent.GetPlayerByName(Title); if (curPlayer != null) { // Update the pip count if (InvokeRequired) { BeginInvoke(new OnUpdatePipCountDelegate(OnUpdatePipCount), curPlayer.PipCount); } else { OnUpdatePipCount(curPlayer.PipCount); } } } }
public bool OnDisconnect(ref string returnMessage) { bool bRetVal = false; if (player != null) { if (gameRoom != null) { gameRoom.Leave(player); gameRoom.RemoveListener(this); player.RemoveListener(this); player = null; bRetVal = true; } else { if (returnMessage != null) { returnMessage += "\nNo game room available to disconnect from!"; } } } else { if (returnMessage != null) { returnMessage += "\nPlayer is not connected!"; } bRetVal = false; } return(bRetVal); }
public void OnEventNotification(INetworkBackgammonNotifier sender, INetworkBackgammonEvent e) { if (InvokeRequired) { Invoke(new NotifyDelegate(OnEventNotification), new object[] { sender, e }); } else { buttonAction.Enabled = false; buttonAction.Text = "[No Action]"; #region Sender: Game Room if (sender is NetworkBackgammonRemoteGameRoom) { try { if (e is NetworkBackgammonGameRoomEvent) { NetworkBackgammonGameRoomEvent gameRoomEvent = (NetworkBackgammonGameRoomEvent)e; switch (gameRoomEvent.EventType) { case NetworkBackgammonGameRoomEvent.GameRoomEventType.PlayerConnected: UpdateConnectedPlayersList(); break; case NetworkBackgammonGameRoomEvent.GameRoomEventType.PlayerDisconnected: UpdateConnectedPlayersList(); break; } } else if (e is NetworkBackgammonChallengeEvent) { NetworkBackgammonChallengeEvent challengeEvent = (NetworkBackgammonChallengeEvent)e; string challengingPlayer = challengeEvent.ChallengingPlayer; string challengedPlayer = challengeEvent.ChallengedPlayer; if (challengingPlayer.CompareTo(player.PlayerName) != 0 && challengedPlayer.CompareTo(player.PlayerName) == 0) { bool challengeResponse = MessageBox.Show( "Accept game challenge from " + challengeEvent.ChallengingPlayer + "?", "Game Challenge", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes; player.RespondToChallenge(challengeResponse, challengeEvent.ChallengingPlayer); groupBoxGameControls.Enabled = challengeResponse; } } } catch (Exception ex) { listBoxLog.Items.Add(ex.Message); } } #endregion #region Sender: Game Session else if (sender is NetworkBackgammonGameSession) { // Filter out broadcasts from our own player if (sender != player) { try { NetworkBackgammonGameSession gameSession = (NetworkBackgammonGameSession)sender; if (e is NetworkBackgammonGameSessionEvent) { // Generate warning if last event has been overwritten // (Currently every event received is supposed to be responded too by means of an // action (e.g. move, dice roll acknowledge, etc), which resets the last event type to invalid) if (lastGameSessionEventType != null) { listBoxLog.Items.Add("Warning: Overwriting last event (" + lastGameSessionEventType.ToString() + ") with new event " + e.GetType().ToString() + " (i.e. last event hasn't been responded to by means of an action)"); } // Latch the event type lastGameSessionEventType = e.GetType(); } #region Actions for initial dice roll if (e is GameSessionInitialDiceRollEvent) { listBoxCheckers.Items.Clear(); listBoxMoves.Items.Clear(); GameSessionInitialDiceRollEvent gameSessionInitialDiceRollEvent = (GameSessionInitialDiceRollEvent)e; // if (player.InitialDice != null) if (gameSessionInitialDiceRollEvent.GetDiceForPlayer(player.PlayerName) != null) { listBoxLog.Items.Add("Initial dice rolled: " + gameSessionInitialDiceRollEvent.GetDiceForPlayer(player.PlayerName).CurrentValue); buttonAction.Enabled = true; buttonAction.Text = "Confirm initial dice roll"; } else { listBoxLog.Items.Add("Warning: Event received is " + e.GetType().ToString() + " but dice values are missing"); } } #endregion #region Actions for checker update else if (e is GameSessionCheckerUpdatedEvent) { listBoxCheckers.Items.Clear(); listBoxMoves.Items.Clear(); GameSessionCheckerUpdatedEvent gameSessionCheckerUpdateEvent = (GameSessionCheckerUpdatedEvent)e; player = gameSessionCheckerUpdateEvent.GetPlayerByName(player.PlayerName); string strDice = ""; foreach (NetworkBackgammonDice d in gameSessionCheckerUpdateEvent.DiceRolled) { strDice += " " + d.CurrentValue; } listBoxLog.Items.Add("Dice: " + strDice); foreach (NetworkBackgammonChecker checker in player.Checkers) { listBoxCheckers.Items.Add(checker); } } #endregion #region Actions for move expected else if (e is GameSessionMoveExpectedEvent) { GameSessionMoveExpectedEvent gameSessionMoveExpectedEvent = (GameSessionMoveExpectedEvent)e; if (player.PlayerName == gameSessionMoveExpectedEvent.ActivePlayer) { listBoxLog.Items.Add("I'm the active player, expected to make the next move ..."); groupBoxGameControls.BackColor = Color.DarkGreen; buttonAction.Enabled = true; buttonAction.Text = "Make Move"; } else { groupBoxGameControls.BackColor = Color.DarkRed; } } #endregion #region Actions for no (valid) move else if (e is GameSessionNoPossibleMovesEvent) { GameSessionNoPossibleMovesEvent gameSessionNoPossibleMovesEvent = (GameSessionNoPossibleMovesEvent)e; if (player.PlayerName == gameSessionNoPossibleMovesEvent.PlayerName) { listBoxLog.Items.Add("I'm the active player, but have no (valid) moves ..."); groupBoxGameControls.BackColor = Color.DarkGreen; buttonAction.Enabled = true; buttonAction.Text = "Confirm"; } else { groupBoxGameControls.BackColor = Color.DarkRed; } } #endregion #region Actions for player resignation else if (e is GameSessionPlayerResignationEvent) { listBoxCheckers.Items.Clear(); listBoxMoves.Items.Clear(); GameSessionPlayerResignationEvent gameSessionPlayerResignationEvent = (GameSessionPlayerResignationEvent)e; listBoxLog.Items.Add("Player " + gameSessionPlayerResignationEvent.ResigningPlayer + " has resigned from current game"); listBoxCheckers.Items.Clear(); groupBoxGameControls.BackColor = SystemColors.Control; groupBoxGameControls.Enabled = false; } #endregion #region Actions for player won game else if (e is GameSessionPlayerWonEvent) { listBoxCheckers.Items.Clear(); listBoxMoves.Items.Clear(); GameSessionPlayerWonEvent gameSessionPlayerWonEvent = (GameSessionPlayerWonEvent)e; if (gameSessionPlayerWonEvent.WinningPlayer == player.PlayerName) { listBoxLog.Items.Add("Yeah!!! I won the game!!!"); } else { listBoxLog.Items.Add("Player " + gameSessionPlayerWonEvent.WinningPlayer + " won the game"); } listBoxCheckers.Items.Clear(); groupBoxGameControls.BackColor = SystemColors.Control; groupBoxGameControls.Enabled = false; } #endregion else if (e is NetworkBackgammonGameSessionEvent) { listBoxCheckers.Items.Clear(); listBoxMoves.Items.Clear(); NetworkBackgammonGameSessionEvent gameSessionEvent = (NetworkBackgammonGameSessionEvent)e; switch (gameSessionEvent.EventType) { case NetworkBackgammonGameSessionEvent.GameSessionEventType.GameFinished: { listBoxLog.Items.Add("Game finished"); } break; } } } catch (Exception ex) { listBoxLog.Items.Add(ex.Message); } } } #endregion } }
/// <summary> /// Calculates possible moves for the checkers of the active player. /// </summary> /// <param name="player1">Player 1</param> /// <param name="player2">Player 2</param> /// <param name="dice">Dice</param> /// <returns>Success (true) if at least one move of the active players checkers is possible, otherwise false.</returns> /// <remarks> /// Updates the active players list of possible moves. /// </remarks> public static bool CalculatePossibleMoves(ref NetworkBackgammonPlayer player1, ref NetworkBackgammonPlayer player2, NetworkBackgammonDice[] _dice) { // Abbreviations: // // AP: Active Player (e.g. checkerHistogramAP -> histogram of checkers of active player) // WP: Waiting Player (e.g. checkerWP -> checkers of the waiting player) // kvp: key-value-pair // Check whether only one player is active if (player1.Active == true && player2.Active == true) { throw new NetworkBackgammonGameEngineException("Both backgammon players cannot be active (it can only by one player's turn)!"); } // Determine the active player NetworkBackgammonPlayer activePlayer = player1.Active ? player1 : player2; NetworkBackgammonPlayer waitingPlayer = player1.Active ? player2 : player1; // Check whether dice values are passed in and if the array has no more than 2 elements and more than 0 elements // and if dice values are valid if (_dice == null) { throw new NetworkBackgammonGameEngineException("Dice values missing (reference to array is null)!"); } else { if (_dice.Count() == 0) { throw new NetworkBackgammonGameEngineException("Dice value array has 0 elements! Need at least on element!"); } else { if (_dice.Count() > 2) { throw new NetworkBackgammonGameEngineException("Too many dice values (passed in " + _dice.Count() + ", expected 1..2)"); } else { foreach (NetworkBackgammonDice diceValue in _dice) { if (diceValue.CurrentValue < NetworkBackgammonDice.DiceValue.MIN || diceValue.CurrentValue > NetworkBackgammonDice.DiceValue.MAX) { throw new NetworkBackgammonGameEngineException("At least one dice has an invalid value (" + diceValue.CurrentValue + ")"); } } } } } // Check whether dice values are the same (tie) -> Use just one dice for all sub-sequent processing steps if it's a tie NetworkBackgammonDice[] diceToUse = _dice; if (_dice.Count() > 1) { if (_dice[0].CurrentValue == _dice[1].CurrentValue) { diceToUse = new NetworkBackgammonDice[] { _dice[0] }; } } bool bMovesCalulationDone = false; bool bAllCheckersHomeOrOffBoard = true; bool bActivePlayerHasMoves = false; #region Preparation // Delete possible moves for both, active ... foreach (NetworkBackgammonChecker checkerAP in activePlayer.Checkers) { checkerAP.PossibleMoves.Clear(); } // ... and waiting player (just to be sure) foreach (NetworkBackgammonChecker checkerWP in waitingPlayer.Checkers) { checkerWP.PossibleMoves.Clear(); } // Check whether all checkers of active player are home or off the board // This is necessary to determine whether active player is able to bear off // checkers (off the board) foreach (NetworkBackgammonChecker checkerAP in activePlayer.Checkers) { if (!((checkerAP.CurrentPosition.CurrentPosition >= NetworkBackgammonPosition.GameBoardPosition.HOME_START && checkerAP.CurrentPosition.CurrentPosition <= NetworkBackgammonPosition.GameBoardPosition.HOME_END) || checkerAP.CurrentPosition.CurrentPosition == NetworkBackgammonPosition.GameBoardPosition.OFFBOARD)) { bAllCheckersHomeOrOffBoard = false; break; } } Dictionary <NetworkBackgammonPosition.GameBoardPosition, UInt32> checkerHistogramAP = new Dictionary <NetworkBackgammonPosition.GameBoardPosition, uint>(); Dictionary <NetworkBackgammonPosition.GameBoardPosition, UInt32> checkerHistogramWP = new Dictionary <NetworkBackgammonPosition.GameBoardPosition, uint>(); // Add checker position histogram containers for all positions checkers are sitting on (for active and waiting player) foreach (NetworkBackgammonPosition.GameBoardPosition positionValue in Enum.GetValues(typeof(NetworkBackgammonPosition.GameBoardPosition))) { if (!checkerHistogramAP.ContainsKey(positionValue)) { checkerHistogramAP.Add(positionValue, 0); } if (!checkerHistogramWP.ContainsKey(positionValue)) { checkerHistogramWP.Add(positionValue, 0); } } // Create checker position histograms foreach (NetworkBackgammonChecker checkerAP in activePlayer.Checkers) { checkerHistogramAP[checkerAP.CurrentPosition.CurrentPosition] += 1; } foreach (NetworkBackgammonChecker checkerWP in waitingPlayer.Checkers) { if (checkerWP.CurrentPosition.GetOppositePosition().CurrentPosition != NetworkBackgammonPosition.GameBoardPosition.INVALID) { checkerHistogramWP[checkerWP.CurrentPosition.GetOppositePosition().CurrentPosition] += 1; } else { checkerHistogramWP[checkerWP.CurrentPosition.CurrentPosition] += 1; } } Dictionary <NetworkBackgammonChecker, KeyValuePair <NetworkBackgammonDice, NetworkBackgammonPosition>[]> potentialPositionHistogram = new Dictionary <NetworkBackgammonChecker, KeyValuePair <NetworkBackgammonDice, NetworkBackgammonPosition>[]>(); // Create potential positions for every checker foreach (NetworkBackgammonChecker checkerAP in activePlayer.Checkers) { if (!potentialPositionHistogram.ContainsKey(checkerAP)) { // List of potential positions associated associated with a certain dice value List <KeyValuePair <NetworkBackgammonDice, NetworkBackgammonPosition> > potPosList = new List <KeyValuePair <NetworkBackgammonDice, NetworkBackgammonPosition> >(); foreach (NetworkBackgammonDice dice in diceToUse) { // Moving a checker off the board (bear off) is only allowed if all checkers of active player are in the home position // (or off the board already) if ((bAllCheckersHomeOrOffBoard) || (checkerAP.CurrentPosition + dice).CurrentPosition < NetworkBackgammonPosition.GameBoardPosition.OFFBOARD) { potPosList.Add(new KeyValuePair <NetworkBackgammonDice, NetworkBackgammonPosition>(dice, checkerAP.CurrentPosition + dice)); } } potentialPositionHistogram.Add( checkerAP, potPosList.ToArray()); } } #endregion // First pass: Check whether any checkers are on the bar // as they'll have to be moved before anything else foreach (NetworkBackgammonChecker checkerAP in activePlayer.Checkers) { // Found a checker that's sitting on the bar if (checkerAP.CurrentPosition.CurrentPosition == NetworkBackgammonPosition.GameBoardPosition.BAR) { bMovesCalulationDone = true; foreach (NetworkBackgammonDice dice in diceToUse) { // Now, see whether opponents checker position allow us to move if (checkerHistogramWP[(checkerAP.CurrentPosition + dice).CurrentPosition] <= 1) { checkerAP.PossibleMoves.Add(dice); bActivePlayerHasMoves = true; } } } } if (!bMovesCalulationDone) { // Second pass: Check whether potential moves are valid foreach (KeyValuePair <NetworkBackgammonChecker, KeyValuePair <NetworkBackgammonDice, NetworkBackgammonPosition>[]> kvp in potentialPositionHistogram) { foreach (KeyValuePair <NetworkBackgammonDice, NetworkBackgammonPosition> kvpDicePos in kvp.Value) { // Check whether waiting player occupies the potential position for active players checker // (i.e. waiting player has more than 1 checker on the potential position) // or potential position is off board (which is allways possible) if (checkerHistogramWP[kvpDicePos.Value.CurrentPosition] <= 1 || kvpDicePos.Value.CurrentPosition == NetworkBackgammonPosition.GameBoardPosition.OFFBOARD) { kvp.Key.PossibleMoves.Add(kvpDicePos.Key); bActivePlayerHasMoves = true; } } } } return(bActivePlayerHasMoves); }
/// <summary> /// Challenge an opponent to a game /// </summary> /// <param name="_challengingPlayer">Challenging backgammon player</param> /// <param name="_challengedPlayer">Challenged backgammon player</param> /// <returns></returns> public bool Challenge(string _challengingPlayerName, string _challengedPlayerName) { bool retval = false; NetworkBackgammonPlayer _challengingPlayer = GetPlayerByName(_challengingPlayerName); NetworkBackgammonPlayer _challengedPlayer = GetPlayerByName(_challengedPlayerName); // Check if the players are in the available player game room list //if (connectedPlayers.Contains(_challengingPlayer) && connectedPlayers.Contains(_challengedPlayer)) if (_challengingPlayer != null && _challengedPlayer != null) { // Check if players are currently free to participate in a game session if (!IsPlayerInGameSession(_challengingPlayer) && !IsPlayerInGameSession(_challengedPlayer)) { Semaphore challengeSemaphore = new Semaphore(0, 1); // Add game challenge data container instance to be used for the (asynchronous) challenge procedure challengeSyncList.Add(_challengedPlayer, new NetworkBackgammonChallengeDataContainer(challengeSemaphore)); // Broadcast the player challenge event Broadcast(new NetworkBackgammonChallengeEvent(_challengingPlayerName, _challengedPlayerName)); // Wait for response from challenged player (or timeout) if (challengeSemaphore.WaitOne(challengeRequestTimeoutMs)) { retval = challengeSyncList[_challengedPlayer].ChallengeAccepted; // Create and start game session if challenge has been accepted by challenged player if (retval) { // Create game session monitor if it doesn't exist yet StartGameSessionMonitor(); // Create game session instance NetworkBackgammonGameSession gameSession = new NetworkBackgammonGameSession(_challengingPlayer, _challengedPlayer); // Append to list of game sessions... gameSessions.Add(gameSession); // Start the game... gameSession.Start(); // Broadcast the game active event to all registered listeners Broadcast(new NetworkBackgammonGameRoomEvent(NetworkBackgammonGameRoomEvent.GameRoomEventType.PlayerPlaying)); } challengeSyncList.Remove(_challengedPlayer); // Give the challenging player the challenge response Broadcast(new NetworkBackgammonChallengeResponseEvent(retval, _challengedPlayerName, _challengingPlayerName)); } else { challengeSyncList.Remove(_challengedPlayer); retval = false; } } } return(retval); }
public void OnEventNotification(INetworkBackgammonNotifier sender, INetworkBackgammonEvent e) { if (sender is NetworkBackgammonPlayer) { NetworkBackgammonPlayer player = (NetworkBackgammonPlayer)sender; if (e is NetworkBackgammonChallengeResponseEvent) { NetworkBackgammonChallengeResponseEvent challengeResponseEvent = (NetworkBackgammonChallengeResponseEvent)e; if (challengeSyncList.Keys.Contains(player)) { if (challengeSyncList[player] != null) { challengeSyncList[player].ChallengeAccepted = challengeResponseEvent.ChallengeAccepted; try { challengeSyncList[player].ChallengeSemaphore.Release(); } catch (SemaphoreFullException ex) { // TODO: If this exception occurs calling Release too many times... } } } } else if (e is NetworkBackgammonChatEvent) { // Pass the message through to the listeners Broadcast((NetworkBackgammonChatEvent)e); } } else if (sender is NetworkBackgammonGameSession) { NetworkBackgammonGameSession gameSession = (NetworkBackgammonGameSession)sender; if (e is NetworkBackgammonGameSessionEvent) { NetworkBackgammonGameSessionEvent gameSessionEvent = (NetworkBackgammonGameSessionEvent)e; if (gameSessionEvent.EventType == NetworkBackgammonGameSessionEvent.GameSessionEventType.GameFinished) { bool newQueueItemAdd = true; NetworkBackgammonEventQueueElement newQueueItem = new NetworkBackgammonEventQueueElement(e, sender); lock (newQueueItem) { /* * if (gameSessionsEventQueue.Count > 0) * { * NetworkBackgammonEventQueueElement lastQueueItem = gameSessionsEventQueue.Last(); * // Avoid adding the same event (from the same sender) twice * // Reason: Game Room listens to events from all players, i.e. also * // both players that are in one Game Session. Thus, all events broadcasted * // by the Game Session arrive here (at the Game Room) twice * if (gameSessionsEventQueue.Last().Notifier == sender && * gameSessionsEventQueue.Last().Event == e) * { * newQueueItemAdd = false; * } * } */ if (newQueueItemAdd) { gameSessionsEventQueue.Enqueue(new NetworkBackgammonEventQueueElement(e, sender)); gameSessionsSemaphore.Release(); } } } } } }
/// <summary> /// Game session state machine (worker thread) /// </summary> private void Run() { GameSessionState currentState = GameSessionState.InitialDiceRoll; NetworkBackgammonEventQueueElement eventQueueElement = null; // Number of moves left for the active player UInt32 activePlayerMovesLeft = 0; bool activePlayerMoveDoubles = false; while (bStateMachineKeepRunning) { // Wait for another event semStateMachine.WaitOne(); // HACK: Add function here to deal with the big switch statement if (!bStateMachineKeepRunning) { break; } // Read the event element queue to get information on the next event to be processed if (eventQueue.Count > 0) { eventQueueElement = eventQueue.Dequeue(); } #region State Machine Pre-Processing // Handle special events that interfere with the regular game play (e.g. player resignation) if (eventQueueElement != null) { if (eventQueueElement.Event is GameSessionPlayerResignationEvent) { currentState = GameSessionState.PlayerResigned; } } #endregion #region State Machine Main Processing switch (currentState) { #region State: Initial Dice Roll case GameSessionState.InitialDiceRoll: { // Initialize both players checkers to their intial configuration // (start positions) player1.InitCheckers(); player2.InitCheckers(); // Use random number generator to figure out which player starts RollDice(); player1.Active = false; player2.Active = false; player1.InitialDice = dice[0]; player2.InitialDice = dice[1]; Broadcast(new GameSessionInitialDiceRollEvent( player1.PlayerName, player1.InitialDice, player2.PlayerName, player2.InitialDice)); currentState = GameSessionState.InitialDiceRollAcknowledgeExpected; } break; #endregion #region State: Initial Dice Roll Acknowledge Expected case GameSessionState.InitialDiceRollAcknowledgeExpected: { if (eventQueueElement != null) { try { if (eventQueueElement.Event is GameSessionInitialDiceRollAcknowledgeEvent) { GameSessionInitialDiceRollAcknowledgeEvent gameSessionEvent = (GameSessionInitialDiceRollAcknowledgeEvent)eventQueueElement.Event; NetworkBackgammonPlayer sendingPlayer = (NetworkBackgammonPlayer)eventQueueElement.Notifier; // Latch (flag) acknowledge of initial dice roll from respective player sendingPlayer.InitialDice.FlagUsed = true; // Check whether both players have acknowledged initial dice roll if (player1.InitialDice.FlagUsed && player2.InitialDice.FlagUsed) { // If dice rolled are a tie, roll again if (dice[0].CurrentValue == dice[1].CurrentValue) { RollDice(); player1.InitialDice = dice[0]; player2.InitialDice = dice[1]; Broadcast(new GameSessionInitialDiceRollEvent( player1.PlayerName, player1.InitialDice, player2.PlayerName, player2.InitialDice)); } else { // Determine active player (the one who won the initial dice roll if (dice[0].CurrentValueUInt32 > dice[1].CurrentValueUInt32) { player1.Active = true; } else { player2.Active = true; } // Calculate number of moves left for active player (based on dice values) activePlayerMoveDoubles = dice[0].CurrentValue == dice[1].CurrentValue; activePlayerMovesLeft = (UInt32)(activePlayerMoveDoubles ? 4 : 2); // Calculate possible moves for active player (and figure out whether active player // actually has possible moves) bool activePlayerHasPossibleMoves = NetworkBackgammonGameEngine.CalculatePossibleMoves( ref player1, ref player2, activePlayerMoveDoubles ? new NetworkBackgammonDice[] { dice[0] } : dice); // Send initial checkers with positions (and possible valid moves // for the active player) to both players Broadcast(new GameSessionCheckerUpdatedEvent(player1, player2, dice[0], dice[1])); if (activePlayerHasPossibleMoves) { // Inform players about who's expected to make the next move Broadcast(new GameSessionMoveExpectedEvent(player1.Active ? player1.PlayerName : player2.PlayerName)); // Set next iteration's state currentState = GameSessionState.MoveExpected; } else { // Inform both players that currently active player has no moves left // which needs to be acknowledged by the active player Broadcast(new GameSessionNoPossibleMovesEvent(player1.Active ? player1.PlayerName : player2.PlayerName)); currentState = GameSessionState.NoPossibleMovesAcknowledgeExpected; } } } } } catch (Exception) { Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error)); } } } break; #endregion #region State: Move Expected case GameSessionState.MoveExpected: { if (eventQueueElement != null) { try { NetworkBackgammonPlayer sendingPlayer = (NetworkBackgammonPlayer)eventQueueElement.Notifier; GameSessionMoveSelectedEvent moveSelectedEvent = (GameSessionMoveSelectedEvent)eventQueueElement.Event; // Check whether the active player attempted to move if (sendingPlayer.Active) { // Perform the selected move of the active player (and check whether the active player won the game if (!NetworkBackgammonGameEngine.ExecuteMove(ref player1, ref player2, moveSelectedEvent.CheckerSelected, moveSelectedEvent.MoveSelected)) { // Figure out the next active player (could be the current // active player since up to 4 moves are allowed per turn) if (--activePlayerMovesLeft <= 0) { player1.Active = !player1.Active; player2.Active = !player2.Active; // Roll dice for active player RollDice(); // Calculate number of moves left for active player (based on dice values) activePlayerMoveDoubles = dice[0].CurrentValue == dice[1].CurrentValue; activePlayerMovesLeft = (UInt32)(activePlayerMoveDoubles ? 4 : 2); } // Calculate possible moves for active player (and figure out whether active player // actually has possible moves) bool activePlayerHasPossibleMoves = NetworkBackgammonGameEngine.CalculatePossibleMoves( ref player1, ref player2, activePlayerMoveDoubles ? new NetworkBackgammonDice[] { dice[0] } : activePlayerMovesLeft == 2 ? dice : new NetworkBackgammonDice[] { dice[0] == moveSelectedEvent.MoveSelected ? dice[1] : dice[0] }); // Send updated checkers with positions (and possible valid moves // for the active player) to both players Broadcast(new GameSessionCheckerUpdatedEvent(player1, player2, dice[0], dice[1])); if (activePlayerHasPossibleMoves) { // Inform players about who's expected to make the next move Broadcast(new GameSessionMoveExpectedEvent(player1.Active ? player1.PlayerName : player2.PlayerName)); } else { // Inform both players that currently active player has no moves left // which needs to be acknowledged by the active player Broadcast(new GameSessionNoPossibleMovesEvent(player1.Active ? player1.PlayerName : player2.PlayerName)); currentState = GameSessionState.NoPossibleMovesAcknowledgeExpected; } } else { // Inform both players that the game has been won by the active player Broadcast(new GameSessionPlayerWonEvent(sendingPlayer.PlayerName)); currentState = GameSessionState.GameFinished; } } else { Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error)); } } catch (Exception) { Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error)); } } } break; #endregion #region State: No Possible Moves Acknowledge Expected case GameSessionState.NoPossibleMovesAcknowledgeExpected: { if (eventQueueElement != null) { try { GameSessionNoPossibleMovesAcknowledgeEvent gameSessionNoPossibleMovesAcknowledgeEvent = (GameSessionNoPossibleMovesAcknowledgeEvent)eventQueueElement.Event; // Make sure that the active player acknowledges (and not the waiting player) if (gameSessionNoPossibleMovesAcknowledgeEvent.PlayerName == (player1.Active ? player1.PlayerName : player2.PlayerName)) { player1.Active = !player1.Active; player2.Active = !player2.Active; // Roll dice for (new) active player RollDice(); // Calculate number of moves left for active player (based on dice values) activePlayerMoveDoubles = dice[0].CurrentValue == dice[1].CurrentValue; activePlayerMovesLeft = (UInt32)(activePlayerMoveDoubles ? 4 : 2); bool activePlayerHasPossibleMoves = NetworkBackgammonGameEngine.CalculatePossibleMoves( ref player1, ref player2, activePlayerMoveDoubles ? new NetworkBackgammonDice[] { dice[0] } : activePlayerMovesLeft == 2 ? dice : new NetworkBackgammonDice[] { dice[0], dice[1] }); // Send updated checkers with positions (and possible valid moves // for the active player) to both players Broadcast(new GameSessionCheckerUpdatedEvent(player1, player2, dice[0], dice[1])); if (activePlayerHasPossibleMoves) { // Inform players about who's expected to make the next move Broadcast(new GameSessionMoveExpectedEvent(player1.Active ? player1.PlayerName : player2.PlayerName)); currentState = GameSessionState.MoveExpected; } else { // Inform both players that currently active player has no moves left // which needs to be acknowledged by the active player Broadcast(new GameSessionNoPossibleMovesEvent(player1.Active ? player1.PlayerName : player2.PlayerName)); } } else { Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error)); } } catch (Exception) { Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error)); } } } break; #endregion #region State: Player Resigned case GameSessionState.PlayerResigned: { if (eventQueueElement != null) { try { NetworkBackgammonPlayer resignedPlayer = (NetworkBackgammonPlayer)eventQueueElement.Notifier; Broadcast(new GameSessionPlayerResignationEvent(resignedPlayer.PlayerName)); currentState = GameSessionState.GameFinished; } catch (Exception ex) { Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.Error)); } } } break; #endregion default: break; } #endregion #region State Machine Post-Processing if (currentState == GameSessionState.GameFinished) { Broadcast(new NetworkBackgammonGameSessionEvent(NetworkBackgammonGameSessionEvent.GameSessionEventType.GameFinished)); bStateMachineKeepRunning = false; } #endregion eventQueueElement = null; } }
/// <summary> /// Determines whether a backgammon player is part of this game session /// </summary> /// <param name="player">Backgammon player to be checked</param> /// <returns>"True" is backgammon player is part of this game session, otherwise "false"</returns> public bool ContainsPlayer(NetworkBackgammonPlayer player) { return((player == player1) || (player == player2)); }