GameService.Sdk.BeziqueCore
1.0.0
.NET 10.0
This package targets .NET 10.0. The package is compatible with this framework or higher.
.NET Standard 2.1
This package targets .NET Standard 2.1. The package is compatible with this framework or higher.
dotnet add package GameService.Sdk.BeziqueCore --version 1.0.0
NuGet\Install-Package GameService.Sdk.BeziqueCore -Version 1.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GameService.Sdk.BeziqueCore" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GameService.Sdk.BeziqueCore" Version="1.0.0" />
<PackageReference Include="GameService.Sdk.BeziqueCore" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GameService.Sdk.BeziqueCore --version 1.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: GameService.Sdk.BeziqueCore, 1.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GameService.Sdk.BeziqueCore@1.0.0
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GameService.Sdk.BeziqueCore&version=1.0.0
#tool nuget:?package=GameService.Sdk.BeziqueCore&version=1.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
BeziqueCore Unity Integration Guide
Public API Reference
Core Classes
| Class | Namespace | Purpose |
|---|---|---|
BeziqueGame |
BeziqueCore |
Main game class - implements IBeziqueGame |
CardHelper |
BeziqueCore |
Static utility for card ID operations |
GameCard |
BeziqueCore |
Implements IGameCard interface |
PlayerHand |
BeziqueCore |
Implements IPlayerHand interface |
Public Interfaces
| Interface | Namespace | Purpose |
|---|---|---|
IBeziqueGame |
BeziqueCore.Interfaces |
Main game interface |
IGameState |
BeziqueCore.Interfaces |
Query game state |
IGameEvent |
BeziqueCore.Interfaces |
Subscribe to game events |
IPlayerHand |
BeziqueCore.Interfaces |
Player data access |
IGameCard |
BeziqueCore.Interfaces |
Card data access |
Enums
// Game phases
public enum GamePhase
{
Dealing, // Cards being dealt
TrumpFlip, // Trump card being revealed
Phase1_Playing, // Main gameplay (deck available)
Phase2_Playing, // Last 9 cards (no deck)
RoundEnd, // Round completed
GameEnd // Game over
}
// Meld types with point values
public enum MeldType
{
Bezique, // Q Spades + J Diamonds = 40 points
DoubleBezique, // 2x Q Spades + 2x J Diamonds = 500 points
FourJacks, // 40 points
FourQueens, // 60 points
FourKings, // 80 points
FourAces, // 100 points
CommonMarriage, // K + Q same suit = 20 points
TrumpMarriage, // K + Q trump suit = 40 points
TrumpRun, // A, 10, K, Q, J trump suit = 150 points
}
IBeziqueGame - Main Game Interface
Namespace: BeziqueCore.Interfaces
public interface IBeziqueGame : IGameState, IGameEvent
{
// Game lifecycle
void Initialize(int playerCount); // Initialize with 2 or 4 players
void StartDealing(); // Begin dealing phase
bool DealNextSet(); // Deal next 3 cards, returns false if complete
void CompleteDealing(); // Finalize dealing and flip trump
void StartNewRound(); // Start a new round (after RoundEnd)
// Gameplay actions
void PlayCard(int playerIndex, byte cardId); // Play a card
void DrawCard(int playerIndex); // Draw from deck (Phase 1 only)
void CreateMeld(int playerIndex, MeldType meldType, byte[] cardIds); // Claim meld
// Queries
bool IsPlayerTurn(int playerIndex); // Check if it's player's turn
IReadOnlyList<byte> GetPlayableCards(int playerIndex); // Get cards in hand
}
Important Notes:
Initialize()must be called before any other methodsStartDealing()begins the deal processDealNextSet()must be called repeatedly until it returnsfalseCompleteDealing()must be called after dealing is done to flip trump and start playGetPlayableCards()returns all cards in hand, not just legally playable cards for the current trick
IGameState - Query Game State
public interface IGameState
{
GamePhase CurrentPhase { get; } // Current game phase
IReadOnlyList<IPlayerHand> Players { get; } // All players
IGameCard? TrumpCard { get; } // The trump card (flipped during deal)
byte? TrumpSuit { get; } // Trump suit (0-3)
int DeckCount { get; } // Cards remaining in deck
int CurrentPlayerIndex { get; } // Whose turn it is
int DealerIndex { get; } // Who dealt this round
}
IGameEvent - Subscribe to Game Events
WARNING: Not all events are currently implemented/firing. See Event Implementation Status.
public interface IGameEvent
{
event Action<int, IReadOnlyList<IGameCard>> CardsDealt; // (playerIndex, cards)
event Action<IGameCard> TrumpCardFlipped; // (trumpCard)
event Action<int> DealerBonusPoints; // (dealerIndex) - 10 points
event Action<int, IGameCard> CardDrawn; // (playerIndex, card) - NOTE: Typo in name
event Action<GamePhase> PhaseChanged; // (newPhase)
event Action<int> TrickComplete; // (winnerIndex) - NOT FIRED
event Action<int, int> MeldPointsAwarded; // (playerIndex, points) - NOT FIRED
event Action<int> RoundEnd; // (winnerIndex) - NOT FIRED
event Action<int> GameEnd; // (winnerIndex) - NOT FIRED
}
Event Implementation Status
| Event | Status | Notes |
|---|---|---|
CardsDealt |
WORKS | Fires during DealNextSet() |
TrumpCardFlipped |
WORKS | Fires during CompleteDealing() |
DealerBonusPoints |
WORKS | Fires if trump is 7 |
CardDrawn |
NOT WORKING | Event defined but never invoked in code |
PhaseChanged |
WORKS | Fires on phase transitions |
TrickComplete |
NOT WORKING | Event defined but never invoked |
MeldPointsAwarded |
NOT WORKING | Event defined but never invoked |
RoundEnd |
NOT WORKING | Event defined but never invoked |
GameEnd |
NOT WORKING | Event defined but never invoked |
IPlayerHand - Player Data
public interface IPlayerHand
{
int PlayerIndex { get; } // Player index (0-based)
IReadOnlyList<IGameCard> Cards { get; } // Cards in hand
int Score { get; } // Current score
bool IsCurrentPlayer { get; } // Is this the current player?
}
IGameCard - Card Data
public interface IGameCard
{
byte CardValue { get; } // 0-31 (see CardHelper for mapping)
byte DeckIndex { get; } // 0-3 (suit/deck index)
byte CardId { get; } // Full card ID
bool IsJoker { get; } // True if card is a joker
}
CardHelper - Card Utilities
Namespace: BeziqueCore
public static class CardHelper
{
public const byte JOKER = 32; // Joker value
public const byte CARDS_PER_DECK = 32; // Cards per deck
public const byte DECK_COUNT = 4; // Number of decks
// Create a card ID from value and deck index
public static byte CreateCardId(byte cardValue, byte deckIndex);
// Extract components from card ID
public static byte GetCardValue(byte cardId); // Returns 0-31
public static byte GetDeckIndex(byte cardId); // Returns 0-3
// Check for joker
public static bool IsJoker(byte cardId);
}
Card ID Format:
- Bits 0-5: Card value (0-31, where 0-31 are standard cards, 32 is joker)
- Bits 6-7: Deck index (0-3)
Offline Integration - Direct SDK Usage
Basic Setup
using BeziqueCore;
using BeziqueCore.Interfaces;
using System;
public class BeziqueOfflineManager
{
private BeziqueGame _game;
private int _localPlayerIndex = 0;
// ===== Initialization =====
public void StartNewGame(int playerCount = 2)
{
_game = new BeziqueGame();
_game.Initialize(playerCount);
_game.StartDealing();
// Subscribe to events
_game.CardsDealt += OnCardsDealt;
_game.TrumpCardFlipped += OnTrumpCardFlipped;
_game.PhaseChanged += OnPhaseChanged;
_game.DealerBonusPoints += OnDealerBonusPoints;
// Deal all cards (3 sets of 3 cards per player)
DealAllCards();
}
private void DealAllCards()
{
while (_game.DealNextSet())
{
// CardsDealt event fires for each player
}
// Complete dealing to flip trump
_game.CompleteDealing();
}
// ===== Event Handlers =====
private void OnCardsDealt(int playerIndex, IReadOnlyList<IGameCard> cards)
{
Console.WriteLine($"Player {playerIndex} dealt {cards.Count} cards");
// Update UI with new cards
}
private void OnTrumpCardFlipped(IGameCard trumpCard)
{
Console.WriteLine($"Trump: Deck {trumpCard.DeckIndex}, Value {trumpCard.CardValue}");
// Update UI with trump suit
}
private void OnPhaseChanged(GamePhase newPhase)
{
Console.WriteLine($"Phase changed to: {newPhase}");
// Update game state UI
}
private void OnDealerBonusPoints(int dealerIndex)
{
Console.WriteLine($"Dealer {dealerIndex} gets 10 points for trump 7!");
}
// ===== Gameplay =====
public bool PlayCard(byte cardId)
{
if (!_game.IsPlayerTurn(_localPlayerIndex))
{
Console.WriteLine("Not your turn!");
return false;
}
var cards = _game.GetPlayableCards(_localPlayerIndex);
if (!cards.Contains(cardId))
{
Console.WriteLine("Card not in hand!");
return false;
}
_game.PlayCard(_localPlayerIndex, cardId);
return true;
}
public void DrawCard()
{
if (_game.CurrentPhase == GamePhase.Phase1_Playing)
{
_game.DrawCard(_localPlayerIndex);
}
}
// ===== Query Methods =====
public List<byte> GetLocalPlayerHand()
{
var result = new List<byte>();
if (_game == null) return result;
var players = _game.Players;
if (_localPlayerIndex >= players.Count) return result;
foreach (var card in players[_localPlayerIndex].Cards)
result.Add(card.CardId);
return result;
}
public int[] GetScores()
{
if (_game == null) return new int[0];
var players = _game.Players;
var scores = new int[players.Count];
for (int i = 0; i < players.Count; i++)
scores[i] = players[i].Score;
return scores;
}
public GamePhase GetCurrentPhase() => _game?.CurrentPhase ?? GamePhase.Dealing;
public int GetCurrentPlayer() => _game?.CurrentPlayerIndex ?? -1;
}
Online Integration (gRPC)
Prerequisites
Install gRPC packages for Unity:
Grpc.Net.ClientGoogle.ProtobufGrpc.Tools(for code generation)
Generate C# code from proto:
protoc -I. --csharp_out . --grpc_out . bezique.proto
gRPC Service API
The server implements the following RPCs:
| RPC | Request | Response | Description |
|---|---|---|---|
StartGame |
PlayerCount |
GameStarted |
Initialize and start dealing |
DealCard |
DealCardRequest |
DealCardResponse |
Deal next set of cards |
GetGameState |
GameStateRequest |
GameState |
Query current state |
PlayCard |
PlayCardRequest |
PlayCardResponse |
Play a card |
Unity gRPC Client Example
using Grpc.Core;
using GrpcService;
using System;
using System.Threading.Tasks;
public class BeziqueOnlineManager
{
private Channel _channel;
private Bezique.BeziqueClient _client;
private int _currentGameId = -1;
public bool IsConnected => _channel?.State == ChannelState.Ready;
public async Task ConnectAsync(string serverAddress = "127.0.0.1:5275")
{
_channel = new Channel(serverAddress, ChannelCredentials.Insecure);
_client = new Bezique.BeziqueClient(_client);
await Task.CompletedTask; // Connection establishes on first call
Console.WriteLine($"Connected to {serverAddress}");
}
public async Task<int> StartGameAsync(int playerCount)
{
var request = new PlayerCount { PlayerCount_ = playerCount };
var response = await _client.StartGameAsync(request);
_currentGameId = response.GameId;
Console.WriteLine(response.GameStartMessage);
return _currentGameId;
}
public async Task<DealCardResponse> DealCardsAsync()
{
var request = new DealCardRequest { GameId = _currentGameId };
return await _client.DealCardAsync(request);
}
public async Task<GameState> GetGameStateAsync()
{
var request = new GameStateRequest { GameId = _currentGameId };
return await _client.GetGameStateAsync(request);
}
public async Task<PlayCardResponse> PlayCardAsync(int playerIndex, int cardId)
{
var request = new PlayCardRequest
{
GameId = _currentGameId,
PlayerIndex = playerIndex,
CardId = cardId
};
return await _client.PlayCardAsync(request);
}
public async Task DisconnectAsync()
{
await _channel.ShutdownAsync();
_channel = null;
}
}
Complete Unity Example
using BeziqueCore;
using BeziqueCore.Interfaces;
using UnityEngine;
using UnityEngine.UI;
public class BeziqueGameManager : MonoBehaviour
{
[Header("Game Settings")]
[SerializeField] private int playerCount = 2;
[SerializeField] private bool useOnlineMode = false;
[Header("UI References")]
[SerializeField] private Text phaseText;
[SerializeField] private Text scoreText;
[SerializeField] private Transform handContainer;
[SerializeField] private GameObject cardPrefab;
private BeziqueGame _game;
private int _localPlayerIndex = 0;
private void Start()
{
if (!useOnlineMode)
{
StartOfflineGame();
}
}
private void StartOfflineGame()
{
_game = new BeziqueGame();
_game.Initialize(playerCount);
// Subscribe to events
_game.CardsDealt += OnCardsDealt;
_game.TrumpCardFlipped += OnTrumpCardFlipped;
_game.PhaseChanged += OnPhaseChanged;
_game.DealerBonusPoints += OnDealerBonus;
// Start dealing
_game.StartDealing();
StartCoroutine(DealAllCards());
}
private System.Collections.IEnumerator DealAllCards()
{
while (_game.DealNextSet())
{
yield return new WaitForSeconds(0.2f); // Visual delay
}
_game.CompleteDealing();
RefreshUI();
}
// ===== Event Handlers =====
private void OnCardsDealt(int playerIndex, IReadOnlyList<IGameCard> cards)
{
if (playerIndex == _localPlayerIndex)
{
Debug.Log($"You received {cards.Count} cards");
}
}
private void OnTrumpCardFlipped(IGameCard trumpCard)
{
Debug.Log($"Trump is deck {trumpCard.DeckIndex}");
RefreshUI();
}
private void OnPhaseChanged(GamePhase phase)
{
phaseText.text = $"Phase: {phase}";
RefreshUI();
}
private void OnDealerBonus(int dealerIndex)
{
Debug.Log($"Dealer {dealerIndex} gets 10 bonus points!");
RefreshUI();
}
// ===== UI Methods =====
public void OnCardClicked(byte cardId)
{
if (_game.IsPlayerTurn(_localPlayerIndex))
{
_game.PlayCard(_localPlayerIndex, cardId);
RefreshUI();
}
}
public void OnDrawButtonClicked()
{
if (_game.IsPlayerTurn(_localPlayerIndex) &&
_game.CurrentPhase == GamePhase.Phase1_Playing)
{
_game.DrawCard(_localPlayerIndex);
RefreshUI();
}
}
private void RefreshUI()
{
// Update scores
string scores = "";
for (int i = 0; i < _game.Players.Count; i++)
{
scores += $"P{i}: {_game.Players[i].Score} ";
}
scoreText.text = scores;
// Update hand
foreach (Transform child in handContainer)
{
Destroy(child.gameObject);
}
var hand = _game.Players[_localPlayerIndex].Cards;
for (int i = 0; i < hand.Count; i++)
{
var cardObj = Instantiate(cardPrefab, handContainer);
var cardUI = cardObj.GetComponent<BeziqueCardUI>();
cardUI.SetCard(hand[i].CardId);
cardUI.OnClick.AddListener(() => OnCardClicked(hand[i].CardId));
}
}
}
Known Limitations
Currently NOT Implemented
| Feature | Status |
|---|---|
Event CardDrawn |
Event defined but never fires in DrawCard() |
Event TrickComplete |
Event defined but never fires |
Event MeldPointsAwarded |
Event defined but never fires |
Event RoundEnd |
Event defined but never fires |
Event GameEnd |
Event defined but never fires |
| Meld Validation | CreateMeld() accepts any meld without card validation |
| Playable Card Validation | GetPlayableCards() returns entire hand, not trick-legal cards |
| Trick Winner Detection | No API to get who won the last trick |
| Turn Management | Must manually track turn order via state polling |
| Network Sync | No built-in networking; use gRPC for online play |
Workarounds
- For
CardDrawnevent: PollDeckCountbefore/after draw to detect if card was drawn - For
TrickComplete: MonitorCurrentPlayerIndexchanges to detect trick end - For meld validation: Implement client-side validation before calling
CreateMeld() - For playable cards: Implement your own logic based on lead card and trump suit
Game Flow Diagram
Initialize(2 or 4)
|
v
StartDealing()
|
+---> DealNextSet() [repeat 3 times per player]
| |
| v
| [CardsDealt event fires]
|
v
CompleteDealing()
|
+---> [TrumpCardFlipped event fires]
|
v
Phase1_Playing
|
+---> PlayCard(player, cardId)
| |
| v
| [Player turn changes]
|
+---> DrawCard(player) [until deck empty]
|
v
Phase2_Playing [when deck empty]
|
+---> PlayCard(player, cardId) [no drawing]
|
v
RoundEnd / GameEnd [events not fired]
Card ID Reference
Deck Layout
- 4 decks (index 0-3), each with 33 cards (32 standard + 1 joker)
- Total: 132 cards
Card Values (0-31 in each deck)
| Value | Meaning |
|---|---|
| 0-31 | Standard card in deck |
| 32 | Joker |
Use CardHelper to convert:
byte cardId = CardHelper.CreateCardId(cardValue: 5, deckIndex: 0);
byte value = CardHelper.GetCardValue(cardId); // 5
byte deck = CardHelper.GetDeckIndex(cardId); // 0
bool isJoker = CardHelper.IsJoker(cardId); // false
Version History
| Version | Date | Changes |
|---|---|---|
| 1.0 | Current | Initial release with basic gameplay, dealing, and state management |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.1
- System.ComponentModel.Annotations (>= 5.0.0)
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 91 | 2/15/2026 |