Last active
July 29, 2024 09:23
-
-
Save OswaldHurlem/2a19e63760cba014b9884ff58205ea95 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <time.h> | |
#include <string.h> | |
struct lbp_serializer | |
{ | |
int32_t DataVersion; | |
FILE* FilePtr; | |
bool IsWriting; | |
}; | |
#define VERSION_IN_RANGE(_from, _to) \ | |
(LbpSerializer->DataVersion >= (_from) && LbpSerializer->DataVersion < (_to)) | |
#define ADD(_fieldAdded, _fieldName) \ | |
if (LbpSerializer->DataVersion >= (_fieldAdded)) \ | |
{ \ | |
(LbpSerializer, &(Datum->_fieldName)); \ | |
} | |
#define ADD_LOCAL(_localAdded, _type, _localName, _defaultValue) \ | |
_type _localName = (_defaultValue); \ | |
if (LbpSerializer->DataVersion >= (_localAdded)) \ | |
{ \ | |
(LbpSerializer, &(_localName)); \ | |
} | |
#define REM(_fieldAdded, _fieldRemoved, _type, _fieldName, _defaultValue) \ | |
_type _fieldName = (_defaultValue); \ | |
if (VERSION_IN_RANGE((_fieldAdded),(_fieldRemoved))) \ | |
{ \ | |
(LbpSerializer, &(_fieldName)); \ | |
} | |
#define MIN(a,b) (((a)<(b))?(a):(b)) | |
#define MAX(a,b) (((a)>(b))?(a):(b)) | |
void Serialize(lbp_serializer* LbpSerializer, int32_t* Datum) | |
{ | |
if (LbpSerializer->IsWriting) | |
{ | |
fwrite(Datum, sizeof(int32_t), 1, LbpSerializer->FilePtr); | |
} | |
else | |
{ | |
fread(Datum, sizeof(int32_t), 1, LbpSerializer->FilePtr); | |
} | |
} | |
enum serialization_versions : int32_t | |
{ | |
SV_Scores = 1, | |
SV_Fouls, | |
SV_ExtraPlayers, | |
SV_AllPlayersInList, | |
SV_FoulsUntracked, | |
// Keep this as the last element | |
SV_LatestPlus1, | |
}; | |
struct pbem_pong_player | |
{ | |
int32_t Points; | |
}; | |
struct pbem_pong_player_list | |
{ | |
int32_t Count; | |
pbem_pong_player* Ptr; | |
}; | |
// (Dumb sample code) + (HMN STL Avoidance Cred) = (don't do this) | |
void Resize(pbem_pong_player_list* List, int32_t NewCount) | |
{ | |
if (List->Count != NewCount) | |
{ | |
if (!List->Ptr) | |
{ | |
List->Ptr = (pbem_pong_player*)malloc(NewCount*sizeof(pbem_pong_player)); | |
} | |
else | |
{ | |
List->Ptr = (pbem_pong_player*)realloc(List->Ptr, NewCount*sizeof(pbem_pong_player)); | |
} | |
for (int32_t i = List->Count; i < NewCount; i++) | |
{ | |
List->Ptr[i] = {}; | |
} | |
List->Count = NewCount; | |
} | |
} | |
// (Dumb sample code) + (HMN STL Avoidance Cred) = (don't do this) | |
void ResizeFromFront(pbem_pong_player_list* List, int32_t NewCount) | |
{ | |
if (List->Count != NewCount) | |
{ | |
int32_t Pad = NewCount - List->Count; | |
if (!List->Ptr) | |
{ | |
List->Ptr = (pbem_pong_player*)malloc(NewCount*sizeof(pbem_pong_player)); | |
} | |
else | |
{ | |
List->Ptr = (pbem_pong_player*)realloc(List->Ptr, NewCount*sizeof(pbem_pong_player)); | |
if (Pad > 0) | |
{ | |
memmove(List->Ptr + Pad, List->Ptr, List->Count*sizeof(pbem_pong_player)); | |
} | |
} | |
for (int32_t i = 0; i < Pad; i++) | |
{ | |
List->Ptr[i] = {}; | |
} | |
List->Count = NewCount; | |
} | |
} | |
void Free(pbem_pong_player_list* List) | |
{ | |
free(List->Ptr); | |
List->Ptr = 0; | |
List->Count = 0; | |
} | |
struct pbem_pong_state | |
{ | |
pbem_pong_player_list AllPlayers; | |
}; | |
void Serialize(lbp_serializer* LbpSerializer, pbem_pong_player* Datum) | |
{ | |
ADD(SV_ExtraPlayers, Points); | |
REM(SV_ExtraPlayers, SV_FoulsUntracked, int32_t, Fouls, 0); | |
Datum->Points = MAX(Datum->Points - Fouls, 0); | |
} | |
void Serialize(lbp_serializer* LbpSerializer, pbem_pong_player_list* Datum) | |
{ | |
ADD_LOCAL(SV_ExtraPlayers, int32_t, NewCount, Datum->Count); | |
Resize(Datum, NewCount); | |
for (int32_t i = 0; i < Datum->Count; i++) | |
{ | |
ADD(SV_ExtraPlayers, Ptr[i]); | |
} | |
} | |
const pbem_pong_player_list EmptyList = {}; | |
void Serialize(lbp_serializer* LbpSerializer, pbem_pong_state* Datum) | |
{ | |
REM(SV_Scores, SV_AllPlayersInList, int32_t, P1Score, 0); | |
REM(SV_Scores, SV_AllPlayersInList, int32_t, P2Score, 0); | |
REM(SV_Fouls, SV_AllPlayersInList, int32_t, P1Fouls, 0); | |
REM(SV_Fouls, SV_AllPlayersInList, int32_t, P2Fouls, 0); | |
REM(SV_ExtraPlayers, SV_AllPlayersInList, pbem_pong_player_list, ExtraPlayers, EmptyList); | |
ADD(SV_AllPlayersInList, AllPlayers); | |
if (VERSION_IN_RANGE(SV_Scores, SV_AllPlayersInList)) | |
{ | |
int32_t PlayersFromLegacyFormat = ExtraPlayers.Count + 2; | |
ResizeFromFront(&Datum->AllPlayers, Datum->AllPlayers.Count + PlayersFromLegacyFormat); | |
Datum->AllPlayers.Ptr[0].Points = MAX(P1Score - P1Fouls, 0); | |
Datum->AllPlayers.Ptr[1].Points = MAX(P2Score - P2Fouls, 0); | |
for (int i = 0; i < ExtraPlayers.Count; i++) | |
{ | |
Datum->AllPlayers.Ptr[2 + i] = ExtraPlayers.Ptr[i]; | |
} | |
Free(&ExtraPlayers); | |
} | |
} | |
void InitPongState(pbem_pong_state* PongState) | |
{ | |
Resize(&PongState->AllPlayers, 2); | |
} | |
void Gameplay(pbem_pong_state* PongState) | |
{ | |
Resize(&PongState->AllPlayers, PongState->AllPlayers.Count + 1); | |
printf("A dark figure emerges from the shadows and removes its cloak. It's Player %d.\n", | |
PongState->AllPlayers.Count); | |
for (int Ind = 0; Ind < PongState->AllPlayers.Count; Ind++) | |
{ | |
pbem_pong_player* Player = PongState->AllPlayers.Ptr + Ind; | |
printf("Player %d has a score of %d\n", Ind+1, Player->Points); | |
if (rand() % 2) | |
{ | |
printf("Player %d fouls! But maybe this will pay off?\n", Ind+1); | |
Player->Points = MAX(Player->Points - 1, 0); | |
} | |
else | |
{ | |
printf("Player %d scores! But he'll need more than that to win.\n", Ind+1); | |
Player->Points++; | |
} | |
printf("Now Player %d has a score of %d\n", Ind+1, Player->Points); | |
} | |
} | |
bool SerializeIncludingVersion(lbp_serializer* LbpSerializer, pbem_pong_state* PongState) | |
{ | |
if (LbpSerializer->IsWriting) | |
{ | |
LbpSerializer->DataVersion = SV_LatestPlus1 - 1; | |
} | |
(LbpSerializer, &LbpSerializer->DataVersion); | |
if (LbpSerializer->DataVersion > (SV_LatestPlus1 - 1)) | |
{ | |
return false; | |
} | |
else | |
{ | |
(LbpSerializer, PongState); | |
return true; | |
} | |
} | |
void main(int32_t, char**) | |
{ | |
int32_t latestVersion = SV_LatestPlus1 - 1; | |
FILE* FilePtr; | |
srand(time(NULL)); | |
bool Success = false; | |
pbem_pong_state PongState = {}; | |
while (!Success) | |
{ | |
printf("Welcome to Pong By Email v%d.\nEnter the PBEM file you'd like to open, " | |
"or & to start a new game\n", latestVersion); | |
char buffer[256]; | |
gets_s(buffer); | |
if (buffer[0] == '&') | |
{ | |
InitPongState(&PongState); | |
Success = true; | |
} | |
else | |
{ | |
if (FilePtr = fopen(buffer, "r")) | |
{ | |
lbp_serializer LbpSerializer; | |
LbpSerializer.IsWriting = false; | |
LbpSerializer.FilePtr = FilePtr; | |
if (SerializeIncludingVersion(&LbpSerializer, &PongState)) | |
{ | |
printf("Opened PBEM file %s (from version %d)\n", buffer, LbpSerializer.DataVersion); | |
Success = true; | |
} | |
else | |
{ | |
printf("Can't read PBEM file %s from version %d\n", buffer, LbpSerializer.DataVersion); | |
} | |
fclose(FilePtr); | |
} | |
else | |
{ | |
printf("Couldn't open %s\n", buffer); | |
} | |
} | |
} | |
Gameplay(&PongState); | |
Success = false; | |
while (!Success) | |
{ | |
printf("Enter name of PBEM file to save\n"); | |
char buffer[256]; | |
gets_s(buffer); | |
if (FilePtr = fopen(buffer, "w")) | |
{ | |
lbp_serializer LbpSerializer; | |
LbpSerializer.IsWriting = true; | |
LbpSerializer.FilePtr = FilePtr; | |
SerializeIncludingVersion(&LbpSerializer, &PongState); | |
fclose(FilePtr); | |
Success = true; | |
} | |
else | |
{ | |
printf("Couldn't open %s\n", buffer); | |
} | |
} | |
printf("Thanks for playing. Look forward to version %d, coming out soon!\n", SV_LatestPlus1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
(explanation forthcoming)