Click here to Skip to main content
15,896,063 members
Articles / Programming Languages / C#

A Game Lobby System in C#

Rate me:
Please Sign up or sign in to vote.
4.73/5 (31 votes)
21 Oct 2008CPOL8 min read 287.9K   3.6K   105  
A simple lobby server for hosting multiple small games and allowing players to create and join games of many types.
// created on 23/11/2005 at 22:25
using System;
using System.Text;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

using RedCorona.Net;
using RedCorona.Net.Games;
using RedCorona.Components;

namespace RedCorona.GameClient.GameTypes.Bridge.UI {
	public class TrickPanel : Panel {
		BridgeGame game;
		internal Trick[] Tricks = new Trick[13];
		internal int currentTrick;
		
		internal TrickPanel(BridgeGame game){
			this.game = game;
		}
		
		internal void Clear(){
			currentTrick = 0;
			game.TricksWon[0] = game.TricksWon[1] = 0;
			Tricks[0] = new Trick((game.declarer + 1) % 4);
			if(nextPhase != null) Controls.Remove(nextPhase);
		}
		
		internal int NextToPlay {
			get {
				if((currentTrick < 0) || (currentTrick > 12)) return -1;
				if(Tricks[currentTrick].played >= 4) return -1;
				return (Tricks[currentTrick].leader + Tricks[currentTrick].played) % 4;
			}
		}
		
		internal void NextPlay(){
			// Do the next action (only called if we own the game)
			ByteBuilder b = new ByteBuilder();
			Invalidate();
			if(currentTrick >= 13) {				
				return;				
			} else if(Tricks[currentTrick].played >= 4){
				BumpTrick();
				Pauser.RunAfterPause(this, new MethodInvoker(NextPlay), 250);
				return;
			}
			BridgePlayer player = game.Players[NextToPlay], actor;
			if(player.isDummy) actor = player.opposite;
			else actor = player;
			if(actor.Player == null){
				// Computer
				int card;
				try {
					Card c = actor.GetCard(Tricks[currentTrick], player);
					Console.WriteLine("Chose "+BridgePlayer.formatCard(c.Value)+" because "+c.Message);
					card = c.Value;
				} catch(Exception e) {
					card = 0;
					Console.WriteLine("wtf ... "+e);
				}
				SubmitCard(Tricks[currentTrick].played, card);
				return;
			} else if(actor.Player.ID == game.Lobby.MyID){
				// It's me, so don't be silly and go off the server
				Invalidate();
			} else {
				b.AddParameter(ClientInfo.UintToBytes(BridgeCommands.RequestCard), ParameterType.Int);
				b.AddParameter(ClientInfo.IntToBytes(player.Player.ID), ParameterType.Int);
				b.AddParameter(ClientInfo.IntToBytes(game.Game.ID), ParameterType.Int);
				game.Lobby.SendMessage(ReservedCodes.PassToMember, b.Read(0, b.Length), 0);
			}
			
		}
		
		void SubmitCard(int posn, int card){
			Console.WriteLine("Submitted card "+card+" at "+posn);
			ByteBuilder b = new ByteBuilder();
			b.AddParameter(ClientInfo.UintToBytes(BridgeCommands.PlayCard), ParameterType.Int);
			b.AddParameter(ClientInfo.IntToBytes(game.Game.ID), ParameterType.Int);
			b.AddParameter(ClientInfo.IntToBytes(posn), ParameterType.Int);
			b.AddParameter(ClientInfo.IntToBytes(card), ParameterType.Int);
			game.Lobby.SendMessage(ReservedCodes.GameBroadcast, b.Read(0, b.Length), 0);
		}		
		
		internal void DoCard(int posn, int card){
			if(posn != Tricks[currentTrick].played)
				game.DoWarning("OOS (DoCard): posn("+posn+") != tpos("+Tricks[currentTrick].played+")");
			int pp = (Tricks[currentTrick].leader + posn) % 4;
			if(!game.Players[pp].cards[card]){
				game.DoWarning("Attempt to play bad card! ("+posn+"/"+BridgePlayer.formatCard(card)+")");
				return;
			}
			if(Tricks[currentTrick].cards[posn] >= 0){
				game.DoWarning("Error (DoCard): card already played by this player");
				return;
			}
			game.Players[pp].remove(card);
			for(int i = 0; i < 4; i++) game.Players[i].cardsgone[card] = true;
			Tricks[currentTrick].cards[posn] = card;
			if(posn == 0){
				Tricks[currentTrick].suit = card / 13;
				if(currentTrick == 0) game.dummy = game.Players[(game.declarer + 2) % 4];
			} else {
				int winningcard = Tricks[currentTrick].WinningCard;
				if((((card / 13) == (winningcard / 13)) && (card > winningcard)) || (((card / 13) == game.trumps) && ((winningcard / 13) != game.trumps))){
					Console.WriteLine("-- New winner "+BridgePlayer.formatCard(card)+" over "+BridgePlayer.formatCard(winningcard));
					Tricks[currentTrick].winning = (Tricks[currentTrick].leader + posn) % 4;
				}
				if(Tricks[currentTrick].suit != card / 13) game.Players[pp].voids[Tricks[currentTrick].suit] = true;
			}
			Tricks[currentTrick].played = posn + 1;
			UpdateText();
			Invalidate();
			
			if((posn == 3) && !game.OwnsGame){
				Pauser.RunAfterPause(this, new MethodInvoker(BumpTrick), 500);
			}
			//Thread.Sleep(300);
		}
		
		void BumpTrick(){
			game.TricksWon[Tricks[currentTrick].winning % 2]++;
			currentTrick++;				
			if(currentTrick < 13) Tricks[currentTrick] = new Trick(Tricks[currentTrick - 1].winning);
			else Finish((int)GameState.Debrief, "Go to the debriefing for this round");
			UpdateText();
		}
		
		String cardMsg = "";
		internal void UpdateText(){
			// We should always run GetCard because if a player then
			// leaves, the computer has all the right information set
			// to carry on (who's shown which suit etc)
			if(NextToPlay < 0) return;
			BridgePlayer pl = game.Players[NextToPlay], actor;
			if(pl.isDummy) actor = pl.opposite;
			else actor = pl;
			Card card = actor.GetCard(Tricks[currentTrick], pl);
			if(actor.Player == null){
				// Computer
				cardMsg = "AI playing from "+Constants.seats[NextToPlay];
			} else if(actor.Player.ID == game.Lobby.MyID){
				Console.WriteLine(cardMsg = "AI Hint � " + BridgePlayer.formatCard(card.Value) + ": " + card.Message);
			} else {
				cardMsg = "Waiting for " + actor.Player.DisplayName + "...";
			}
			if((currentTrick < 13) &&	(Tricks[currentTrick].played == 4)){
				currentTrick++;
			}			
			if((currentTrick >= 13)){
				Finish((int)GameState.Debrief, "Click here to continue");				
				return;
			}
		}
		
		// UI
		
		Font f = new Font("Arial", 8.5F);
		Font fbold = new Font("Arial", 8.5F, FontStyle.Bold);
		Font fbig = new Font("Arial", 14);
		Brush blackBrush = new SolidBrush(Color.Black);
		float[] cardx = new float[4], cardy = new float[4];
		protected override void OnPaint(PaintEventArgs e){
			Graphics g = e.Graphics;
			
			int suit = -1;
			if((currentTrick < 13) && (currentTrick >= 0)) suit = Tricks[currentTrick].suit;
			if(game.CanSee(0)) game.Players[0].PaintHand(g, (ClientSize.Width / 2) - 50, 20, game.IsControlled(0) && (NextToPlay == 0), suit);
			if(game.CanSee(1)) game.Players[1].PaintHand(g, ClientSize.Width - 100, (ClientSize.Height / 2) - 30, game.IsControlled(1) && (NextToPlay == 1), suit);
			if(game.CanSee(2)) game.Players[2].PaintHand(g, (ClientSize.Width / 2) - 50, ClientSize.Height - 70, game.IsControlled(2) && (NextToPlay == 2), suit);
			if(game.CanSee(3)) game.Players[3].PaintHand(g, 10, (ClientSize.Height / 2) - 30, game.IsControlled(3) && (NextToPlay == 3), suit);
			
			cardx[0] = cardx[2] = (ClientSize.Width / 2) - 15;
			cardx[3] = 110; cardx[1] = ClientSize.Width - 140;
			cardy[1] = cardy[3] = (ClientSize.Height / 2) - 8;
			cardy[0] = 85; cardy[2] = ClientSize.Height - 100;
			
			g.DrawString("Declarer: ", fbold, blackBrush, ClientSize.Width - 100, 10);
			g.DrawString(game.Players[game.declarer].ToString(), f, blackBrush, ClientSize.Width - 100, 20);
			
			g.DrawString("Contract: ", fbold, blackBrush, ClientSize.Width - 100, 33);
			PaintContract(g, game.contract, ClientSize.Width - 100, 43);			
			
			g.DrawString("Tricks won: ", fbold, blackBrush, 10, 10);
			g.DrawString("N/S: "+game.TricksWon[0]+"; E/W: "+game.TricksWon[1], f, blackBrush, 10, 20);
			
			g.DrawString(cardMsg, f, blackBrush, 5, ClientSize.Height - 15);
			
			if((currentTrick < 13) && (currentTrick >= 0))
				for(int i = 0; i < Tricks[currentTrick].played; i++){
					int inx = (i + Tricks[currentTrick].leader) % 4;
					int card = Tricks[currentTrick].cards[i];
					g.DrawImage(Constants.suitimages[card / 13], cardx[inx], cardy[inx]);
					g.DrawString(Constants.cardnames[card % 13], fbig, blackBrush, cardx[inx] + 15, cardy[inx]);
				}
		}
		
		Button nextPhase;
		void Finish(int buttonState, String caption){
			nextPhase = new Button();
			nextPhase.Text = caption;
			nextPhase.Font = f;
			nextPhase.Left = 0; nextPhase.Top = ClientSize.Height - 20;
			nextPhase.Width = ClientSize.Width; nextPhase.Height = 20;
			nextPhase.Tag = buttonState;
			nextPhase.Click += new EventHandler(nextPhaseClick);
			nextPhase.Enabled = game.OwnsGame;
			Controls.Add(nextPhase);
		}
		
		void nextPhaseClick(object sender, EventArgs e){
			game.SetData("state", (int)(sender as Control).Tag, false, 1);
			(sender as Control).Enabled = false;
		}
		
		internal void PaintContract(Graphics g, Contract c, float x, float y){
			//g.DrawString(BidPanel.getLevel(c.bid).ToString(), f, blackBrush, x, y);			
			//g.DrawImage(Constants.suitimagessmall[BidPanel.getSuit(c.bid)], x + 8, y - 1);
			String s = BidPanel.getLevel(c.bid) + Constants.suits_short[BidPanel.getSuit(c.bid)];
			switch(c.doublestate){
				case 0: break;
				case 1:
					s += " doubled";//, f, blackBrush, x + 20, y);
					break;
				case 2:
					s += " redoubled";//, f, blackBrush, x + 20, y);
					break;
			}
			g.DrawString(s, f, blackBrush, x, y);			
		}
		
		protected override void OnMouseDown(MouseEventArgs e){
			base.OnMouseDown(e);
			if(NextToPlay < 0) return;
			for(int i = 0; i < 13; i++){
				ActiveRegion ar = game.Players[NextToPlay].regions[i];
				if(ar == null) continue;
				if((e.X >= ar.Left) && (e.X <= ar.Left + ar.Width) && (e.Y >= ar.Top) && (e.Y <= ar.Top + ar.Height)){
					SubmitCard(Tricks[currentTrick].played, ar.card);
					break;
				}
			}
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
I'm a recent graduate (MSci) from the University of Cambridge, no longer studying Geology. Programming is a hobby so I get to write all the cool things and not all the boring things Smile | :) . However I now have a job in which I have to do a bit of work with a computer too.

Comments and Discussions