C++筆記 撲克牌問題


題目

許多撲克牌遊戲都需要向4位玩家發牌,發完牌後每個人會有13張牌。有的玩家可以不整牌直接玩,但是大多數玩家都需要整牌。整牌首先按花色排序,花色順序為:
C = Clubs♣ < D = Diamonds♢ < S = Spades♠ < H = Hearts♡

再來按照點數大小排序,點數順序為:
2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < T < J < Q < K < A

玩家的座位分為北N,東E,南S,西W。其中一位玩家將被指定為發牌人,他向每個玩家分發一張牌,從左手邊的玩家開始,然後順時針進行發牌。請你寫一個程式,該程式將讀取一副撲克牌的順序,並將其發給4位玩家,最後根據最後一行指令輸出對應格式的檔案。

(1) 程式要求:
A. 教過的函式庫皆可用,基本上 iostream、vector、map、fstream這四個就夠用了,另外map如果需要歷遍,請避免使用auto。
B. 請讀取input.txt中的資料
C. 依照input.txt最後給出的數字執行相對應的功能,並將之輸出於”output.txt”

(2) 資料解說
"input.txt"的第一行為一個字元(N,E,S,W),代表發牌人坐的方位。
接下來二行為一副撲克牌順序,每行26張牌,每張牌中間使用空白做間隔。

(3) 輸出格式:
功能1請輸出一個字元代表拿到梅花三的玩家並換行。
功能2和功能3輸出皆為四行。分別代表4位玩家的手牌,每張手牌請用空白分隔(每位玩家最後一張牌後面接換行而非空白),輸出玩家順序為(S、W、N、E),最後一行要換行。

//input.txt自此始,但不包括此行
S
CQ DT C4 D8 S7 HT DA H7 D2 S3 D6 C6 S6 D9 S4 SA D7 H2 CK H5 D3 CT S8 C9 H3 C3
DQ S9 SQ DJ H8 HA S2 SK D4 H4 S5 C7 SJ C8 DK C5 C2 CA HQ CJ ST H6 HK H9 D5 HJ
//input.txt至此止,但不包括此comment

//output.txt自此始,但不包括此行
S: D8 H7 C6 SA H5 C9 S9 HA H4 C8 CA H6 HJ
W: CQ S7 D2 S6 D7 D3 H3 SQ S2 S5 DK HQ HK
N: DT HT S3 D9 H2 CT C3 DJ SK C7 C5 CJ H9
E: C4 DA D6 S4 CK S8 DQ H8 D4 SJ C2 ST D5
//output.txt至此止,但不包括此comment

完整程式碼

#include<iostream>
#include<fstream>
#include<string>
#include <string.h>
#include <sstream>
#include<vector>

using namespace std;

vector<string> split(const string &s, char delimiter) {
    vector<string> tokens;     
    string token;     
    istringstream tokenStream(s);     
    while (getline(tokenStream, token, delimiter)) {      
        tokens.push_back(token);     
    }     
    return tokens;  
}

int main(){

    //1. 初始要使用到的變數

        //發牌順序
    string direction[] = {"S","W","N","E"};
        //發牌者
    char licenser;
        //所有卡片
    vector<string> Allcards;
        //每個人持有的卡片
    vector<string> S_hold;
    vector<string> W_hold;
    vector<string> N_hold;
    vector<string> E_hold;

    //2.讀檔知道 licenser、Allcards

    ifstream reader;
    reader.open("P15_input.txt",ios::binary);

    string line;
    while(getline(reader,line)){

        vector<string> tokens = split(line, ' ');

        //判斷line是start還是卡片內容
        if(tokens.size()==1){
            licenser=line[0];
        }
        else{
            for(int i=0;i<tokens.size();i++){
                //去除可能有的\n (e.g. "C3+\n")
                string card = tokens[i].substr(0,2);
                Allcards.push_back(card);
            }
        }
    }
    reader.close();

    //3.依據cards,direction內容完成四個人的持有卡片

        //知道licenser在direction array中的index (發牌的起始方位)
    int start;
    switch(licenser){
        case 'S':
            start = 0;
            break;
        case 'W':
            start = 1;
            break;
        case 'N':
            start = 2;
            break;
        case 'E':
            start = 3;
            break;

        default:
            break;
    }

    for(int i=0;i<Allcards.size();i++){

        //拿牌的人在direction array中的index
        int getCard = (start+1+i)%4;

        switch(getCard){
            case 0:
                S_hold.push_back(Allcards[i]);
                break;
            case 1:
                W_hold.push_back(Allcards[i]);
                break;
            case 2:
                N_hold.push_back(Allcards[i]);
                break;
            case 3:
                E_hold.push_back(Allcards[i]);
                break;

            default:
                break;
        }
    }

    //4.把持有卡片內容寫入output file中
    ofstream outputer;
    outputer.open("P15_output.txt");

        //無法寫入的處理程序

    if (!outputer.is_open()) {
        cout << "Failed to open file.\n";
        return 1; // EXIT_FAILURE
    }

        //可以寫入的程序處理

    for(int i=0;i<4;i++){

        //要被output的玩家在direction array中的index
        int outputCard = (start+i)%4;

        switch (outputCard)
        {
        case 0:
            outputer << "S:";
            for(int j=0;j<S_hold.size();j++){
                outputer << " " << S_hold[j];
            }
            break;
        case 1:
            outputer << "W:";
            for(int j=0;j<E_hold.size();j++){
                outputer << " " << W_hold[j];
            }
            break;
        case 2:
            outputer << "N:";
            for(int j=0;j<N_hold.size();j++){
                outputer << " " << N_hold[j];
            }
            break;
        case 3:
            outputer << "E:";
            for(int j=0;j<E_hold.size();j++){
                outputer << " " << E_hold[j];
            }
            break;

        default:
            break;
        }

        outputer << '\n';
    }

    outputer.close();
}

解題觀念及思路

設定裝讀取值的變數

        //發牌順序
    string direction[] = {"S","W","N","E"};
        //發牌者
    char licenser;
        //所有卡片
    vector<string> Allcards;
        //每個人持有的卡片
    vector<string> S_hold;
    vector<string> W_hold;
    vector<string> N_hold;
    vector<string> E_hold;
  1. 首先要讀入誰是發牌者 --> licenser
  2. 把所有卡片都先記下來 --> vector{string} Allcards

                    --> 再發給各玩家 vecotr{string} S_hold、W_hold等
    

    3.建立方位陣列 string direction[] = {"S","W","N","E"} , 作為發牌順序判斷

    ## 取得input資料內容

    接下來就是讀取檔案內容

    ifstream reader;
    reader.open("P15_input.txt",ios::binary);
    
    string line;
    while(getline(reader,line)){
    
        vector<string> tokens = split(line, ' ');
    
        //判斷line是start還是卡片內容
        if(tokens.size()==1){
            licenser=line[0];
        }
        else{
            for(int i=0;i<tokens.size();i++){
                //去除可能有的\n (e.g. "C3+\n")
                string card = tokens[i].substr(0,2);
                Allcards.push_back(card);
            }
        }
    }
    reader.close();
    

    其中檔案讀取的line我們用line長度來判斷是發牌者還是卡片

    if(tokens.size()==1){
            licenser=line[0];
        }
        else{
            for(int i=0;i<tokens.size();i++){
                //去除可能有的\n (e.g. "C3+\n")
                string card = tokens[i].substr(0,2);
                Allcards.push_back(card);
            }
        }
    

    其中要特別強調:
    由於用split切割成的string 可能會含有'\n'
    e.g. 上面input.txt切割完後 會得到字串 "C3\n" "HJ\n"
    因此我們面對被切割後的只取前兩個char ,如此一來就可以避免存到"\n"
    導致後面output的時候遇到問題

    string card = tokens[i].substr(0,2);
    

    這時 發牌者licenser 、 所有卡片資訊 Allcards值已經填好!

    ## 根據licenser、Allcards來發牌

    首先我們先將 發牌者licenser
    和剛剛的方位矩陣string direction[] = {"S","W","N","E"}作對應
    用start記住 licenser在{"S","W","N","E"}的對應座標

    int start;
    switch(licenser){
        case 'S':
            start = 0;
            break;
        case 'W':
            start = 1;
            break;
        case 'N':
            start = 2;
            break;
        case 'E':
            start = 3;
            break;
    
        default:
            break;
    }
    

    接下來開始發牌
    每張牌會從start+1(start的左邊) 依序直到發完

    for(int i=0;i<Allcards.size();i++){
    
        //拿牌的人在direction array中的index
        int getCard = (start+1+i)%4;
    
        switch(getCard){
            case 0:
                S_hold.push_back(Allcards[i]);
                break;
            case 1:
                W_hold.push_back(Allcards[i]);
                break;
            case 2:
                N_hold.push_back(Allcards[i]);
                break;
            case 3:
                E_hold.push_back(Allcards[i]);
                break;
    
            default:
                break;
        }
    }
    

用餘數的方式就可以實現照順序發牌
e.g. 假設start一開始為S --> 0
(0+1+0)%4 = 1 --> W 拿牌
(0+1+1)%4 = 2 --> N 拿牌
(0+1+2)%4 = 3 --> E 拿牌
(0+1+3)%4 = 0 --> S 拿牌
(0+1+4)%4 = 1 --> W 拿牌
(0+1+5)%4 = 2 --> N 拿牌 ...

 int getCard = (start+1+i)%4;

把S、W、N、E 的牌組輸出到output.txt

此時S_hold、W_hold等已經存好卡片

 ofstream outputer;
    outputer.open("P15_output.txt");

        //無法寫入的處理程序

    if (!outputer.is_open()) {
        cout << "Failed to open file.\n";
        return 1; // EXIT_FAILURE
    }

        //可以寫入的程序處理

    for(int i=0;i<4;i++){

        //要被output的玩家在direction array中的index
        int outputCard = (start+i)%4;

        switch (outputCard)
        {
        case 0:
            outputer << "S:";
            for(int j=0;j<S_hold.size();j++){
                outputer << " " << S_hold[j];
            }
            break;
        case 1:
            outputer << "W:";
            for(int j=0;j<E_hold.size();j++){
                outputer << " " << W_hold[j];
            }
            break;
        case 2:
            outputer << "N:";
            for(int j=0;j<N_hold.size();j++){
                outputer << " " << N_hold[j];
            }
            break;
        case 3:
            outputer << "E:";
            for(int j=0;j<E_hold.size();j++){
                outputer << " " << E_hold[j];
            }
            break;

        default:
            break;
        }

        outputer << '\n';
    }

    outputer.close();







你可能感興趣的文章

[ week 4 ] 網路基礎-TCP/IP

[ week 4 ] 網路基礎-TCP/IP

CS50 Lec7 - SqLite SameTime Update Error - Transaction

CS50 Lec7 - SqLite SameTime Update Error - Transaction

DSA-01 note

DSA-01 note






留言討論