The purpose of the lesson
Today we will write a network card game for two players. What game to write? Let's write a popular card game "Fool", the purpose of which is to get rid of all the cards. You can learn more about the rules here .

Picture 1.
Bit of theory
â RS-232, , , . "", , . RS-232, M5STACK TTL.
RS-232 "0" -3 -25 , "1" +3 +25 . RS-232 TTL "0" 0 , "1" , 3.3 5 (. 2).

2. RS-232 TTL
M5STACK / R0, R2 (RX) T0, T2 (TX) (. 3), USB . , , 0 1 .

3.
Serial Arduino IDE https://www.arduino.cc/reference/en/language/functions/communication/serial/
ASCII, : https://en.wikipedia.org/wiki/ASCII
- M5STACK (2 .);
- USB-C (2 .);
- .
!
1. â
, . , MS Office Word Paint (. 4).

4. MS Office Word Paint :)
1.2.1 http://forum.m5stack.com/topic/49/lesson-1-2-1-lcd-how-to-create-image-array logo.c, .
extern unsigned char logo[];
2.
, 36 . 6 . 12 . 10 , . , 7. : ( â ; ; ; ) : , , , , . , . (. 5).

5.
define playerAmount 6
#define fullAmount 36
#define playerFieldAmount 10
#define gameFieldAmount 6
struct playCard
{
int card;
int suit;
int color;
int property = -1; // -2 not avaiable, -1 free, 0 trump, 1 player #1, 2 player #2
int field; // + player field, - game field
};
String cards[9] = {"6", "7", "8", "9", "10", "J", "Q", "K", "A"};
char suits[4] = {3, 4, 5, 6}; // , , ,
int colors[2] = {0xe8e4, 0x00}; // red, black
playCard deckCards[fullAmount];
3.
. 1. , , (. 5.1):

5.1.
if ((opponentSteps == playerSteps) && (playerSteps == 0))
{
deckCards[selectedCard].field = -1;
sync();
}
. 2. , , (. 5.2) , :

5.1.
else if (opponentSteps > playerSteps)
{
if ((((deckCards[selectedCard].card > deckCards[pfCardId].card) && (deckCards[selectedCard].suit == deckCards[pfCardId].suit)) || ((deckCards[selectedCard].suit == trumpSuit) && (deckCards[pfCardId].suit != trumpSuit))))
{
deckCards[selectedCard].field = -opponentSteps;
sync();
}
}
. 3. , , :
else if (opponentSteps == playerSteps)
{
for (int i = 0; i < fullAmount; i++)
{
if ((deckCards[selectedCard].card == deckCards[i].card) && (deckCards[i].field < 0) && (deckCards[i].property != -2))
{
deckCards[selectedCard].field = -(playerSteps + 1);
sync();
break;
}
}
}
/. 1. , :
if (opponentSteps == playerSteps) //
{
for (int i = 0; i < fullAmount; i++)
{
if (deckCards[i].field < 0)
{
deckCards[i].property = -2;
}
}
}
/. 2. , , :
else if (opponentSteps > playerSteps) //
{
for (int i = 0; i < fullAmount; i++)
{
if ((deckCards[i].field < 0) && (deckCards[i].property != -2))
{
addPlayerCard(playerId, i);
}
}
}
4.
M5STACK, .
void drawCard(int x, int y, playCard card) {
M5.Lcd.fillRoundRect(x, y, 30, 50, 5, 0xffff);
M5.Lcd.drawRoundRect(x, y, 30, 50, 5, 0x00);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(colors[card.color]);
M5.Lcd.setCursor((x + 3), (y + 6));
M5.Lcd.print(cards[card.card]);
M5.Lcd.setTextSize(3);
M5.Lcd.setCursor((x + 8), (y + 24));
M5.Lcd.print(suits[card.suit]);
}
void drawEmptyCard(int x, int y) {
M5.Lcd.fillRect(x, y, 30, 50, 0x2589);
M5.Lcd.drawRoundRect(x, y, 30, 50, 5, 0x00);
}
Windows: Alt 3 6, .
5.
void drawSelect(playCard card) {
int n = card.field - 1;
int x = 10 + (n * 30);
int y = (yPlayerField - 10);
drawEmptyCard(x, yPlayerField);
drawCard(x, y, card);
}
void clearSelect(playCard card) {
int n = card.field - 1;
int x = 10 + (n * 30);
int y = (yPlayerField - 10);
M5.Lcd.fillRect(x, y, 30, 50, 0x2589);
drawCard(x, yPlayerField, card);
}
6.
void drawGameTable() {
M5.Lcd.fillScreen(0x2589); // green table
drawAllFields();
}
void drawMenu() {
M5.Lcd.fillRect(0, 0, 320, 20, 0x00); // score bar
M5.Lcd.fillRect(0, 220, 320, 20, 0x00); // menu bar
/* menu buttons */
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(0x7bef);
M5.Lcd.setCursor(62, 223);
M5.Lcd.print("G");
M5.Lcd.setCursor(155, 223);
M5.Lcd.print("A");
M5.Lcd.setCursor(247, 223);
M5.Lcd.print("S");
}
7.
void updateGraphics() {
drawGameTable();
drawMenu();
drawPlayerId();
drawRest();
for (int i = 0; i < fullAmount; i++)
{
if (deckCards[i].property == playerId)
{
if (deckCards[i].field > 0) // if in the hands of
drawPlayerCard(deckCards[i]);
else // if in the playing field
drawPlPfCard(deckCards[i]);
}
else if (deckCards[i].property == getOpponentId())
{
if (deckCards[i].field < 0)
drawOpPfCard(deckCards[i]); // draw opponent's cards in the playing field
}
else if (deckCards[i].property == 0)
{
drawTrumpCard(deckCards[i]);
}
}
}
(, drawPlayerId()) ;)
8. /

5.3
. , . , true false.
bool serialWriteStr(String str) {
Serial.print(str);
Serial.print('\n');
String input = serialReadStr();
if (input != "")
{
if (input[0] == (char)0x04) return true;
}
return false;
}
timeout (3000 ) , . .
String serialReadStr() {
String buf = "";
long timeout = 3000;
long previousMillis = millis();
while (true)
{
unsigned long currentMillis = millis();
if (currentMillis - previousMillis > timeout) break;
if (Serial.available() > 0)
{
char chr = (char)Serial.read();
if ((chr != (char)0x16) && (chr != (char)0x04) && (buf == "")) // clear trash
{
Serial.read();
}
else
{
if (chr == '\n')
break;
else
buf += chr;
}
}
}
if (buf != "")
{
Serial.print((char)0x04);
Serial.print('\n');
}
return buf;
}
9.
, , (. 6). , , . (parseString), , ( Split JS).

6.
bool serialRecivePacket() {
String input = serialReadStr();
if (input.length() > 0)
{
if ((char)input[0] == (char)0x16)
{
input[0] = ' ';
char groupType = ((parseString(0, (char)0x1d, input)).toInt()) + '0';
String groupData = parseString(1, (char)0x1d, input);
int groupDataLenmo = (groupData.length() - 1);
if (groupData[groupDataLenmo] == (char)0x03)
{
groupData[groupDataLenmo] = ' ';
if (groupType == (char)0x31) updateReciveDeckCards(groupData);
else if (groupType == (char)0x32) playerId = 2;
return true;
}
}
return false;
}
}
String parseString(int idSeparator, char separator, String str) { // like split JS
String output = "";
int separatorCout = 0;
for (int i = 0; i < str.length(); i++)
{
if ((char)str[i] == separator)
{
separatorCout++;
}
else
{
if (separatorCout == idSeparator)
{
output += (char)str[i];
}
else if (separatorCout > idSeparator)
{
break;
}
}
return output;
}
parseString:
parseString(1, ':', "cat:Bob,Jack:dog:Martin,Kelvin"); -----> Bob,Jack
parseString(0, ',', "Bob,Jack"); -----> Bob
parseString(1, ',', "Bob,Jack"); -----> Jack
10.
, , , , " !" (. 7). - . , " !" 2.

7.
void handshakePlayerId() {
long tpid = random(10, 1001) * 10;
long previousMillis = millis();
while (!serialRecivePacket())
{
unsigned long currentMillis = millis();
if (currentMillis â previousMillis > tpid) break;
}
while (!serialSendPlayerTId());
}
bool serialSendPlayerTId() {
String str = "";
str += (char)0x16;
str += (char)0x32; // type "player id selector"
str += (char)0x1d;
str += (char)0x03;
return serialWriteStr(str);
}
11.
bool serialSendDeckCards() {
String str = "";
str += (char)0x16;
str += (char)0x31; // type "card transfer flag"
str += (char)0x1d;
for (int i = 0; i < fullAmount; i++)
{
str += (char)0x1e;
str += deckCards[i].card;
str += (char)0x1f;
str += deckCards[i].suit;
str += (char)0x1f;
str += deckCards[i].color;
str += (char)0x1f;
str += deckCards[i].property;
str += (char)0x1f;
str += deckCards[i].field;
}
str += (char)0x03;
return serialWriteStr(str);
}
void updateReciveDeckCards(String groupData) {
for (int i = 0; i < fullAmount; i++)
{
/* update new card */
String record = parseString(i, (char)0x1e, groupData);
deckCards[i].card = (int8_t)(parseString(0, (char)0x1f, record).toInt());
deckCards[i].suit = (int8_t)(parseString(1, (char)0x1f, record).toInt());
deckCards[i].color = (int8_t)(parseString(2, (char)0x1f, record).toInt());
deckCards[i].property = (int8_t)(parseString(3, (char)0x1f, record).toInt());
deckCards[i].field = (int8_t)(parseString(4, (char)0x1f, record).toInt());
}
getTrumpCard();
updateGraphics();
checkWinner();
}
(, getTrumpCard()) ;)
12.
. . loop(). . .
void sync(bool auto_ = false) {
if (queue != playerId)
{
while (!serialRecivePacket());
queue = playerId;
}
else
{
if (!auto_)
{
while (!serialSendDeckCards());
updateGraphics();
checkWinner();
queue = getOpponentId();
}
}
}
13. ?
whoWin() , -1, .
, , . 10 (playerFieldAmount) , .
int whoWin() {
int opponentId = getOpponentId();
int playerAmount_ = getAmount(playerId);
int opponentAmount = getAmount(opponentId);
if ((playerAmount_ == 0) || (opponentAmount >= playerFieldAmount)) return playerId;
if ((opponentAmount == 0) || (playerAmount_ >= playerFieldAmount)) return opponentId;
return -1;
}
void checkWinner() {
int winner = whoWin();
if (winner != -1) drawWinnerShow(winner);
}
14.
: , beta-, . !
Arduino IDE .
15.
(. 5.1). (. 8). ! (. 8.1).

8.

8.1. !
\
M5Stack