Tujuan pelajaran
Hari ini kita akan menulis permainan kartu jaringan untuk dua pemain. Game apa yang harus ditulis? Mari kita menulis permainan kartu populer "Bodoh", yang tujuannya adalah untuk menyingkirkan semua kartu. Anda dapat mempelajari lebih lanjut tentang aturan di sini .

Gambar 1.
Sedikit teori
— 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