Hi all. This is my first ever post, so let me know if I do anything wrong so I can learn. I am writing code to create Battleboats, which is essentially Battleship, on a MicroChip PIC32. When I run my code, sometimes I can play one or two turns before I get a message parsing protocol error. My code is hundreds of lines long, and I am having trouble figuring out what is wrong. Sometimes the game will not even start at all. I think my issue lies in my ProtocolDecode() and ProtocolGetTurnOrder() functions, so I am going to post these two, with comments describing exactly how they are supposed to work. If you need any other code, or header files that contain the structures and enumerations I am using, just let me know. Thank you!
Code:
/**
* This function decodes a message into either the NegotiationData or GuessData structs depending
* on what the type of message is. This function receives the message one byte at a time, where the
* messages are in the format defined by MESSAGE_TEMPLATE, with payloads of the format defined by
* the PAYLOAD_TEMPLATE_* macros. It returns the type of message that was decoded and also places
* the decoded data into either the `nData` or `gData` structs depending on what the message held.
* The onus is on the calling function to make sure the appropriate structs are available (blame the
* lack of function overloading in C for this ugliness).
*
* PROTOCOL_PARSING_FAILURE is returned if there was an error of any kind (though this excludes
* checking for NULL pointers), while
*
* @param parser The struct holding the data for this instance of the parser. Allows for multiple
* protocol decoding streams.
* @param in The next character in the NMEA0183 message to be decoded.
* @param nData A struct used for storing data if a message is decoded that stores NegotiationData.
* @param gData A struct used for storing data if a message is decoded that stores GuessData.
* @return A value from the UnpackageDataEnum enum.
*/
ProtocolParserStatus ProtocolDecode(char in, NegotiationData *nData, GuessData *gData)
{
switch (parserState) {
case WAITING:
if (in == '$') {
parserIndex = 0;
parserState = RECORDING;
return PROTOCOL_PARSING_GOOD;
} else {
return PROTOCOL_WAITING;
}
break;
case RECORDING:
if (parserIndex > PROTOCOL_MAX_PAYLOAD_LEN) {
parserState = WAITING;
return PROTOCOL_PARSING_FAILURE;
} else if (in == '*') {
parserState = FIRST_CHECKSUM_HALF;
parserIndex++;
return PROTOCOL_PARSING_GOOD;
} else {
parserPayload[parserIndex] = in;
parserIndex++;
return PROTOCOL_PARSING_GOOD;
}
break;
case FIRST_CHECKSUM_HALF:
// If valid hex char
if (((in >= '0') && (in <= '9')) || ((in >= 'a') && (in <= 'f'))
|| ((in >= 'A') && (in <= 'F'))) {
parserCheckSum[0] = in;
parserState = SECOND_CHECKSUM_HALF;
return PROTOCOL_PARSING_GOOD;
} else {
parserState = WAITING;
return PROTOCOL_PARSING_FAILURE;
}
break;
case SECOND_CHECKSUM_HALF:
// If valid hex cha
if (((in >= '0') && (in <= '9')) || ((in >= 'a') && (in <= 'f'))
|| ((in >= 'A') && (in <= 'F'))) {
parserCheckSum[1] = in;
parserState = NEWLINE;
// calculates chcksum and compares to decoded checksum
uint8_t calculatedCheckSum;
uint8_t decodedCheckSum;
if(parserPayload[0] == 'C' && parserPayload[1] == 'O'){
parserPayload[7] = '\0';
}
parserPayload[7] = '\0';
calculatedCheckSum = CheckSum(parserPayload);
decodedCheckSum = CheckSumDecode(parserCheckSum);
if (calculatedCheckSum == decodedCheckSum) {
parserPayload[parserIndex] = '\0';
parserState = NEWLINE;
return PROTOCOL_PARSING_GOOD;
} else {
parserState = WAITING;
return PROTOCOL_PARSING_FAILURE;
}
} else {
parserState = WAITING;
return PROTOCOL_PARSING_FAILURE;
}
break;
case NEWLINE:
if (in == '\n') {
char *identifier;
identifier = strtok(parserPayload, "$,*");
//identifier = strtok(NULL, ",*");
if (strcmp(identifier, "COO") == 0) {
gData->row = atoi(strtok(NULL, ",*"));
gData->col = atoi(strtok(NULL, ",*"));
parserState = WAITING;
return PROTOCOL_PARSED_COO_MESSAGE;
} else if (strcmp(identifier, "HIT") == 0) {
gData->row = atoi(strtok(NULL, ",*"));
gData->col = atoi(strtok(NULL, ",*"));
gData->hit = atoi(strtok(NULL, ",*"));
parserState = WAITING;
return PROTOCOL_PARSED_HIT_MESSAGE;
} else if (strcmp(identifier, "CHA") == 0) {
nData->encryptedGuess = atoi(strtok(NULL, ",*"));
nData->hash = atoi(strtok(NULL, ",*"));
parserState = WAITING;
return PROTOCOL_PARSED_CHA_MESSAGE;
} else if (strcmp(identifier, "DET") == 0) {
nData->guess = atoi(strtok(NULL, ",*"));
nData->encryptionKey = atoi(strtok(NULL, ",*"));
parserState = WAITING;
return PROTOCOL_PARSED_DET_MESSAGE;
} else {
parserState = WAITING;
return PROTOCOL_PARSING_FAILURE;
}
} else {
parserState = WAITING;
return PROTOCOL_PARSING_FAILURE;
}
break;
}
return PROTOCOL_PARSING_FAILURE;
}
/**
* This function returns a TurnOrder enum type representing which agent has won precedence for going
* first. The value returned relates to the agent whose data is in the 'myData' variable. The turn
* ordering algorithm relies on the XOR() of the 'encryptionKey' used by both agents. The least-
* significant bit of XOR(myData.encryptionKey, oppData.encryptionKey) is checked so that if it's a
* 1 the player with the largest 'guess' goes first otherwise if it's a 0, the agent with the
* smallest 'guess' goes first. The return value of TURN_ORDER_START indicates that 'myData' won,
* TURN_ORDER_DEFER indicates that 'oppData' won, otherwise a tie is indicated with TURN_ORDER_TIE.
* There is no checking for NULL pointers within this function.
* @param myData The negotiation data representing the current agent.
* @param myData The negotiation data representing the opposing agent.
* @return A value from the TurnOrdering enum representing which agent should go first.
*/
TurnOrder ProtocolGetTurnOrder(const NegotiationData *myData, const NegotiationData *oppData)
{
uint8_t highLowDeterminer = 0;
highLowDeterminer = ((myData->encryptionKey >> 8) ^ (myData->encryptionKey) ^
(oppData->encryptionKey >> 8) ^ (oppData->encryptionKey)) & 0x01;
// If highLowDeterminer == TRUE highest guess goes first
if (highLowDeterminer) {
if (myData->guess > oppData->guess) {
return TURN_ORDER_START;
} else if (myData->guess == oppData->guess) {
return TURN_ORDER_TIE;
} else {
return TURN_ORDER_DEFER;
}
} // Lowest guess goes first
else {
if (myData->guess < oppData->guess) {
return TURN_ORDER_START;
} else if (myData->guess == oppData->guess) {
return TURN_ORDER_TIE;
} else {
return TURN_ORDER_DEFER;
}
}
}