Help writing a 2D array

This is a discussion on Help writing a 2D array within the C Programming forums, part of the General Programming Boards category; I have a tic tac toe program I am working on and only have one section left to finish. However ...

  1. #1
    Registered User
    Join Date
    Oct 2012
    Location
    Forest Hill, Maryland, United States
    Posts
    39

    Help writing a 2D array

    I have a tic tac toe program I am working on and only have one section left to finish. However I am stuck and need some help. Could someone please help me out here?
    All is complete except the VictoryCheck()
    Please read instuctions.

    INSTRUCTIONS
    Write an implementation for the function VictoryCheck() in for your Tic-Tac-Toe program. The function should take two arguments. The first argument is a single integer argument that contains the number of adjacent symbols a player must produce to win the game. The second argument should be a two-dimensional array representing the board. The number of adjacent symbols a player needs to produce the win the game is defined in the constant CONSECUTIVE_MARKS_REQUIRED. VictoryCheck() should scan through the board and determine if a player has produced the appropriate number of adjacent symbols required to win the game. The adjacent symbols can be oriented in a horizontal, vertical or diagonal fashion. VictoryCheck() should return one of the provided victory codes to the driver. Please use the provided driver code to test your function for correctness. Take note that a scenario where the two players reach a draw in the game is not provided. You SHOULD NOT modify the provided driver code. However, the drive can be modified if you wish to code a board population scenario to produced a TIE condition. Please read the provided driver code carefully before beginning to attack this problem to understand the organizational structure and identifier names you must use in your program. In particular, pay close attention to the function DisplayVictoryMessage(), which has been written for you.



    OK. NOW HERE IS WHAT I HAVE DONE SO FAR:

    Code:
    // INCLUDES
    #include <stdio.h>
     
    // DEFINES
    #ifndef __TRUE_FALSE__
    #define __TRUE_FALSE__
    #define TRUE 1
    #define FALSE 0
    #endif
     
    // ROWS and COLS must be between 1 and 9
    #define ROWS 7
    #define COLS 7
     
    // MARKER CODES
    #define MARKONE 'X'
    #define MARKTWO 'O'
    #define BLANK ' '
     
    // VICTORY CODES
    #define NOWIN 0 // MARKONE and MARKTWO have not produced CONSECUTIVE_MARKS_REQUIRED
     // adjacent symbols and there are moves available on the board
    #define MARKONEVICTORY 1 // MARKONE has produced CONSECUTIVE_MARKS_REQUIRED adjacent symbols
    #define MARKTWOVICTORY 2 // MARKTWO has produced CONSECUTIVE_MARKS_REQUIRED adjacent symbols
    #define TIE 3 // MARKONE and MARKTWO have not produced CONSECUTIVE_MARKS_REQUIRED
     // adjacent symbols and there are no moves available on the board
    #define ERROR 4 // MARKONE and MARKTWO have produced CONSECUTIVE_MARKS_REQUIRED adjacent symbols
    #define EPIC_FAIL 5 // the logic in VictoryCheck() has produced an impossible combination of
     // return code indicators
     
    // GAME PARAMETER CODES
    #define CONSECUTIVE_MARKS_REQUIRED 3
     
    // PROTOTYPES
    void InitializeBoard(char[ROWS][COLS]);
    void DisplayBoard(char[ROWS][COLS]);
    int PlayerMove(int, int, char[ROWS][COLS], char);
    int VictoryCheck(int, char[ROWS][COLS]);
    void DisplayVictoryMessage(int);
     
    // MAIN
    int main() {
     // declare variables
     char board[ROWS][COLS];
     
     // PRODUCE A NOWIN CONDITION
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(1, 1, board, MARKONE);
     PlayerMove(1, 2, board, MARKONE);
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // PRODUCE A HORIZONTAL VICTORY
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(1, 1, board, MARKONE);
     PlayerMove(1, 2, board, MARKONE);
     PlayerMove(1, 3, board, MARKONE);
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // PRODUCE A VERTICAL VICTORY
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(1, 1, board, MARKTWO);
     PlayerMove(2, 1, board, MARKTWO);
     PlayerMove(3, 1, board, MARKTWO);
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // PRODUCE A DIAGONALDOWN VICTORY
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(1, 1, board, MARKONE);
     PlayerMove(2, 2, board, MARKONE);
     PlayerMove(3, 3, board, MARKONE);
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // PRODUCE A DIAGONALUP VICTORY
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(3, 1, board, MARKTWO);
     PlayerMove(2, 2, board, MARKTWO);
     PlayerMove(1, 3, board, MARKTWO);
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // PRODUCE A MULTIPLE PLAYER VICTORY
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(4, 1, board, MARKONE);
     PlayerMove(4, 2, board, MARKONE);
     PlayerMove(4, 3, board, MARKONE);
     
     PlayerMove(3, 1, board, MARKTWO);
     PlayerMove(2, 2, board, MARKTWO);
     PlayerMove(1, 3, board, MARKTWO);
     
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // exit program
     return 0;
    }
     
    // FUNCTION IMPLEMENTATIONS
    void InitializeBoard(char board[ROWS][COLS]) {
      
        for(int i=0; i<ROWS; i++ ) {
            for(int j=0; j<COLS; j++ ) {
                board[i][j]= BLANK;   
            }
        }
    }
     
    void DisplayBoard(char board[ROWS][COLS]) {
       
    
    
    printf("*************TIC-TAC-TOE**********\n\n");
    
    
        printf( "  " );
        for(int i=0; i<COLS; i++ ) {
            printf( "%d ", i + 1 );
        }
            printf( "\n\n" );
    
    
          
        for(int i=0; i<ROWS; i++ ) {
            printf( "%d ", i + 1);
        
            for(int j=0; j<COLS-1; j++ ) {
                printf( "%c|", board[i][j] );
            }
    
    
        printf( "\n  -" );
        for(int j=0; j<COLS-1; j++ ) {
            printf( "+-" , board[i][j]);
        }
        printf( "\n" );
    
    
        }
    
    
    }
     
    int PlayerMove(int row, int col, char board[ROWS][COLS], char symbol) {
      
    
    
        if ( row <= 0 || col <= 0 || row >= ROWS || col >= COLS ) {
            printf( "THE MOVE IS NOT ON THE BOARD\n" );
            return FALSE;
        }
    
    
        else if (board[row-1][col-1] == MARKONE || 
                 board[row-1][col-1] == MARKTWO) {
                printf( "THAT SPACE IS ALREADY OCCUPIED\n" );
                return FALSE;
            
        }
    
    
        else {
            board[row-1][col-1]=symbol;
            return TRUE;
        }
    
    
    
    
        return FALSE;
     
    }
     
    int VictoryCheck(int winRequirement, char board[ROWS][COLS]) {
     // YOUR IMPLEMENTATION GOES HERE
    
    
         
    
    
    
    
        return ERROR;
     
    }
     
    void DisplayVictoryMessage(int victoryCode) {
     // display the victory condition results
     switch(victoryCode) {
     case NOWIN:
     printf("There is still no winner.\n");
     break;
     
     case MARKONEVICTORY:
     printf("MARKONE has won the game.\n");
     break;
     
     case MARKTWOVICTORY:
     printf("MARKTWO has won the game.\n");
     break;
     
     case TIE:
     printf("The game is a draw.\n");
     break;
     
     case ERROR:
     printf("Something bad happened... MARKONE and MARKTWO have both won.\n");
     break;
     
     case EPIC_FAIL:
     printf("Something bad happened... VictoryCheck() has produced an impossible combination of return code indicators.\n");
     break;
     
     default:
     printf("DisplayVictoryMessage() was passed an invalid victoryCode.\n");
     }
    }

  2. #2
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    That's a thorough description of what you have, but doesn't include the part we need the most - what are you stuck on?

    You need to work on that VictoryCheck() function, and if/when you get stuck on a problem, post back. Right now, you have nothing coded in there.

  3. #3
    Registered User
    Join Date
    Oct 2012
    Location
    Forest Hill, Maryland, United States
    Posts
    39
    Quote Originally Posted by Adak View Post
    That's a thorough description of what you have, but doesn't include the part we need the most - what are you stuck on?

    You need to work on that VictoryCheck() function, and if/when you get stuck on a problem, post back. Right now, you have nothing coded in there.

    That is my problem. my victoryCheck function needs to be populated. I don't want someone just to give the answer, but I am not sure how to start. I understand that I need a single integer argument that contains number of moves needed to win game.
    Then I need to write a 2D array that represents the board.

    Would the first argument be as simple as:

    winRequirement = CONSECUTIVE_MARKS_REQUIRED;


    I guess I need to start there before i get to the array.

    THANKS

  4. #4
    Registered User
    Join Date
    Oct 2012
    Location
    Forest Hill, Maryland, United States
    Posts
    39
    Quote Originally Posted by dodgetech View Post
    That is my problem. my victoryCheck function needs to be populated. I don't want someone just to give the answer, but I am not sure how to start. I understand that I need a single integer argument that contains number of moves needed to win game.
    Then I need to write a 2D array that represents the board.

    Would the first argument be as simple as:

    winRequirement = CONSECUTIVE_MARKS_REQUIRED;


    I guess I need to start there before i get to the array.

    THANKS

    I really didn't expect any help here....however I thought I would try. I wasn't looking for THE answer only HELP and TEACHING for someone.

  5. #5
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,501
    You know if

    Code:
    board[0][0] && board[0][1] && board[0][2]
    ... are the same entry, it is true that you have a winner. Why not just go through the 8 different possibilities for victories using if statements for each player?...
    Fact - Beethoven wrote his first symphony in C

  6. #6
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    First blush is to use two for loops: one for checking if any row has a winning 3 chars, and another for loop for checking the 3 columns to see if any of those are a winner.

    Then two if statements for checking the two diagonals.

    Think of it this way - you're the computer - how do YOU check for a win while playing TTT? Repeat it a few times just with paper and pen, and notice the patterns YOU use. Your computer can do it the same way, as long as your check doesn't emphasize senses which the computer doesn't have (vision, for instance).

    After every move, check two things: is the move given, legal, and is the game over because the last move was the winning move or last possible move?

  7. #7
    Registered User
    Join Date
    Oct 2012
    Location
    Forest Hill, Maryland, United States
    Posts
    39
    Quote Originally Posted by Adak View Post
    First blush is to use two for loops: one for checking if any row has a winning 3 chars, and another for loop for checking the 3 columns to see if any of those are a winner.

    Then two if statements for checking the two diagonals.

    Think of it this way - you're the computer - how do YOU check for a win while playing TTT? Repeat it a few times just with paper and pen, and notice the patterns YOU use. Your computer can do it the same way, as long as your check doesn't emphasize senses which the computer doesn't have (vision, for instance).

    After every move, check two things: is the move given, legal, and is the game over because the last move was the winning move or last possible move?

    So then it would be an if, ifelse, and else statements for checking the board? This would be a 2D array statement like my instructions stated? Basically I was given this program and have had to populate all the functions except the last one. My professor said my three previous functions are fine. I just need to make sure I follow the directions which stated I needed a single argument and a 2D array that represents the board.
    THANKS

  8. #8
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,501
    So then it would be an if, ifelse, and else statements for checking the board?
    That would work
    Fact - Beethoven wrote his first symphony in C

  9. #9
    Registered User
    Join Date
    Oct 2012
    Location
    Forest Hill, Maryland, United States
    Posts
    39

    got a couple errors any advice?

    Quote Originally Posted by Click_here View Post
    That would work
    Here is what I got now for the VictoryCheck function, but I am getting 2 errors, both are identier not found. They are for the helpcheckwin and movesleft. Don't they need to point somewhere?

    Code:
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(1, 1, board, MARKTWO);
     PlayerMove(2, 1, board, MARKTWO);
     PlayerMove(3, 1, board, MARKTWO);
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // PRODUCE A DIAGONALDOWN VICTORY
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(1, 1, board, MARKONE);
     PlayerMove(2, 2, board, MARKONE);
     PlayerMove(3, 3, board, MARKONE);
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // PRODUCE A DIAGONALUP VICTORY
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(3, 1, board, MARKTWO);
     PlayerMove(2, 2, board, MARKTWO);
     PlayerMove(1, 3, board, MARKTWO);
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // PRODUCE A MULTIPLE PLAYER VICTORY
     // initialize board
     InitializeBoard(board);
     // populate board
     PlayerMove(4, 1, board, MARKONE);
     PlayerMove(4, 2, board, MARKONE);
     PlayerMove(4, 3, board, MARKONE);
     
     PlayerMove(3, 1, board, MARKTWO);
     PlayerMove(2, 2, board, MARKTWO);
     PlayerMove(1, 3, board, MARKTWO);
     
     // display the board
     DisplayBoard(board);
     // display victory message
     DisplayVictoryMessage( VictoryCheck(CONSECUTIVE_MARKS_REQUIRED, board) );
     
     // exit program
     return 0;
    }
     
    // FUNCTION IMPLEMENTATIONS
    void InitializeBoard(char board[ROWS][COLS]) {
     // YOUR PREVIOUS IMPLEMENTATION GOES HERE
        for(int i=0; i<ROWS; i++ ) {
            for(int j=0; j<COLS; j++ ) {
                board[i][j]= BLANK;   
            }
        }
    }
     
    void DisplayBoard(char board[ROWS][COLS]) {
     // YOUR PREVIOUS IMPLEMENTATION GOES HERE 
    
    
    printf("*************TIC-TAC-TOE**********\n\n");
    
    
        printf( "  " );
        for(int i=0; i<COLS; i++ ) {
            printf( "%d ", i + 1 );
        }
            printf( "\n\n" );
    
    
          
        for(int i=0; i<ROWS; i++ ) {
            printf( "%d ", i + 1);
        
            for(int j=0; j<COLS-1; j++ ) {
                printf( "%c|", board[i][j] );
            }
    
    
        printf( "\n  -" );
        for(int j=0; j<COLS-1; j++ ) {
            printf( "+-" , board[i][j]);
        }
        printf( "\n" );
    
    
        }
    
    
    }
     
    int PlayerMove(int row, int col, char board[ROWS][COLS], char symbol) {
     // YOUR PREVIOUS IMPLEMENTATION GOES HERE
    
    
        if ( row <= 0 || col <= 0 || row >= ROWS || col >= COLS ) {
            printf( "THE MOVE IS NOT ON THE BOARD\n" );
            return FALSE;
        }
    
    
        else if (board[row-1][col-1] == MARKONE || 
                 board[row-1][col-1] == MARKTWO) {
                printf( "THAT SPACE IS ALREADY OCCUPIED\n" );
                return FALSE;
            
        }
    
    
        else {
            board[row-1][col-1]=symbol;
            return TRUE;
        }
    
    
    
    
        return FALSE;
     
    }
     
    int VictoryCheck(int winRequirement, char board[ROWS][COLS]) {
     // YOUR IMPLEMENTATION GOES HERE
    
    
    winRequirement = CONSECUTIVE_MARKS_REQUIRED;
    int P1Wins=0,P2Wins=0;
    int cnt[2]={0};//count, 0-P1, 1-P2
    int i,j,k;
        
    //horizontal
        for(int i=0;i<ROWS;i++){
            for(int j=0;j<COLS;j++){
                if(board[i][j]==MARKONE){
                    P1Wins+=helpcheckWin(cnt,0,winRequirement);
                }
                else if(board[i][j]==MARKTWO){
                        P2Wins+=helpcheckWin(cnt,1,winRequirement);
                }
                else{
                    cnt[0]=0;
                    cnt[1]=0;
                }
    
    
            }//end for j
        }//end for i
    
    
    //vertical
    cnt[0]=0;
    cnt[1]=0;
        for(int j=0;j<COLS;j++){
            for(int i=0;i<ROWS;i++){
                if(board[i][j]==MARKONE){
                    P1Wins+=helpcheckWin(cnt,0,winRequirement);
                }
                else if(board[i][j]==MARKTWO){
                        P2Wins+=helpcheckWin(cnt,1,winRequirement);
                }
                else{
                    cnt[0]=0;
                    cnt[1]=0;
                }
            }//end for j
        }//end for i
    
    
    //FILE *fo;
    //fo=fopen("trace.txt","w");
    
    
    ////////////////////////////////////////////////////////
    //diagonal left
    cnt[0]=0;
    cnt[1]=0;
    
    
    i=0;
    
    
        for(int j=0;j<=COLS-winRequirement;j++){
        // fprintf(fo,"\n\n%d\n",i);
            for(int k=0;k+j<COLS &&k+i<ROWS;k++){
        // fprintf(fo,"\nChecking [%d,%d]",i+k,j+k);
                if(board[i+k][j+k]==MARKONE){
                    P1Wins+=helpcheckWin(cnt,0,winRequirement);
                }
                else if(board[i+k][j+k]==MARKTWO){
                        P2Wins+=helpcheckWin(cnt,1,winRequirement);
                }
                else{
                    cnt[0]=0;
                    cnt[1]=0;
                }
            }
    
    
        }
    cnt[0]=0;
    cnt[1]=0;
    j=0;
        for(int i=1;i<=ROWS-winRequirement;i++){
        //fprintf(fo,"\n\n%d\n",i);
            for(int k=0;k+j<COLS &&k+i<ROWS;k++){
            // fprintf(fo,"\nChecking [%d,%d]",i+k,j+k);
                if(board[i+k][j+k]==MARKONE){
                    P1Wins+=helpcheckWin(cnt,0,winRequirement);
                    }
                    else if(board[i+k][j+k]==MARKTWO){
                            P2Wins+=helpcheckWin(cnt,1,winRequirement);
                    }
                    else{
                        cnt[0]=0;
                        cnt[1]=0;
                    }
            }
    
    
        }
    
    
    ////////////////////////////////////////////////////////
    //diagonal right
    
    
    cnt[0]=0;
    cnt[1]=0;
    
    
    i=0;
    
    
        for(j=COLS-1;j-winRequirement+1>=0;j--){
        //fprintf(fo,"\n\n%d\n",i);
            for(k=0;j-k>=0 &&k+i<ROWS;k++){
            // fprintf(fo,"\nChecking [%d,%d]",i+k,j-k);
                if(board[i+k][j-k]==MARKONE){
                    P1Wins+=helpcheckWin(cnt,0,winRequirement);
                }
                else if(board[i+k][j-k]==MARKTWO){
                        P2Wins+=helpcheckWin(cnt,1,winRequirement);
                }
                else{
                    cnt[0]=0;
                    cnt[1]=0;
                }
            }
    
    
        }
    cnt[0]=0;
    cnt[1]=0;
    
    
    j=COLS-1;
        for(i=1;i<=ROWS-winRequirement;i++){
        //fprintf(fo,"\n\n%d\n",i);
            for(k=0;j-k>=0 &&k+i<ROWS;k++){
            //fprintf(fo,"\nChecking [%d,%d]",i+k,j-k);
                if(board[i+k][j-k]==MARKONE){
                    P1Wins+=helpcheckWin(cnt,0,winRequirement);
                }
                else if(board[i+k][j-k]==MARKTWO){
                    P2Wins+=helpcheckWin(cnt,1,winRequirement);
                }
                else{
                    cnt[0]=0;
                    cnt[1]=0;
                }
            }
    
    
        }
    //fclose(fo);
    
    
    //__________________________________________
        if(P1Wins==0&&P2Wins==0){
            if(movesleft(board)==1)
                return NOWIN;
            else
                return TIE;
        }
        else if(P1Wins>0&&P2Wins==0){return MARKONEVICTORY;}
        else if(P1Wins==0&&P2Wins>0){return MARKTWOVICTORY;}
        else if(P1Wins>0&&P2Wins>0){return ERROR;}
        else
            return EPIC_FAIL;
    
    
    }//end ck
    
    
    int helpcheckWin(int arr[],int idx, int req){ //returns either 1 for win or 0 for not win
    arr[idx]++; //increment this idx
    arr[(idx+1)%2]=0; //set other idx to 0
    
    
        if(arr[idx]==req){
            arr[idx]=0; //reset to 0
            return 1; //return 1 to be added to win
        }
    
    
    return 0;//if not a win, return 0
    }
    int movesleft(char b[ROWS][COLS]){
    int i,j;
        for(i=0;i<ROWS;i++){
            for(j=0;j<COLS;j++){
                if(b[i][j]==BLANK)
                    return 1;
            }
        }
        return 0;//if no BLANK found
     
    }
     
    void DisplayVictoryMessage(int victoryCode) {
     // display the victory condition results
     switch(victoryCode) {
     case NOWIN:
     printf("There is still no winner.\n");
     break;
     
     case MARKONEVICTORY:
     printf("MARKONE has won the game.\n");
     break;
     
     case MARKTWOVICTORY:
     printf("MARKTWO has won the game.\n");
     break;
     
     case TIE:
     printf("The game is a draw.\n");
     break;
     
     case ERROR:
     printf("Something bad happened... MARKONE and MARKTWO have both won.\n");
     break;
     
     case EPIC_FAIL:
     printf("Something bad happened... VictoryCheck() has produced an impossible combination of return code indicators.\n");
     break;
     
     default:
     printf("DisplayVictoryMessage() was passed an invalid victoryCode.\n");
     }
    }

  10. #10
    Registered User
    Join Date
    Oct 2012
    Location
    Forest Hill, Maryland, United States
    Posts
    39
    NEVERMIND

    I forgot to declareabove:

    Code:
    //helper fxn
    int helpcheckWin(int[],int, int);
    int movesleft(char[ROWS][COLS]);
    
    


    THANKS



Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Writing to my 2D Array
    By qwertysingh in forum C Programming
    Replies: 0
    Last Post: 04-12-2009, 02:16 PM
  2. Writing to a 2D array
    By qwertysingh in forum C Programming
    Replies: 3
    Last Post: 03-12-2009, 08:24 AM
  3. Writing array, to file
    By zootreeves in forum C Programming
    Replies: 9
    Last Post: 09-08-2007, 06:06 PM
  4. Writing an array to a file
    By HAssan in forum C Programming
    Replies: 3
    Last Post: 10-24-2005, 07:25 AM
  5. writing an array
    By RedZippo in forum C++ Programming
    Replies: 1
    Last Post: 04-11-2004, 03:47 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21