rummy-demo

Rummy Demo is a sample game developed for Android with backend built over AppWarpS2. Complete code is provided as part of the download.

Rummy is a traditional card game. Here are the rules of this sample game:

  1. Cards will be distributed by the server for a two or three user game
  2. In case of a two-user game, each user will get 9 cards
  3. In case of a three-user game, each user will get 6 cards
  4. A user can select a new card or can pick the top card only once in his turn
  5. After arranging the cards, a user has to do send move
  6. Users have to build combinations of 3 cards
  7. A valid combination is one in which either the cards are of the same Suit and in a Sequence or all the three cards have the same number

Example combinations

Same face value combination: {1,1,1} {K,K,K} {7,7,7}

Same color sequence combination: {1,2,3} {J,Q,K} {Q,K,1}

Note You first need to create an application zone through the admin dashboard. This is described on our Getting Started page.

Client Side

login

Android Client has three screens (Activity)

  1. MainActivity: User enters his name and connects to AppWarpS2 or he can also login with Facebook
  2. RoomSelectionActivity: User has option to join a two or a three user game. When user clicks any button, this first tries to join an existing room if not found it creates a new room and waits for other user/users to join the game
  3. GameActivity: This activity contains complete gameplay logic
  4. The rooms created from the client are turn-based rooms

How to play:

rummy game play

Please refer this video link

rummy game over

Server Work

To run this server side application, please first go through Running your first application. You need to follow the same steps for this sample as well. Following are details specific to this sample.

  1. Start the Game: The game is started when the number of joined users is equal to the desired number (2 or 3)
  2. Deal Cards: Server deals the cards and sends to every user only his cards
  3. Draw New Card: Server provides a new card when client requests a new card from the deck
  4. Validate Move: When client sends any move, the server will validate the cards in the move
  5. Finish Game: After receiving submit cards request, the server will validate the combinations and broadcast the result

Implementation

On server side we have defined extensions to override default server side functionality.

BaseServerAdapter: RummyServerExtension.java

BaseZoneAdapter: RummyZoneExtension.java

BaseTurnRoomAdaptor: RummyRoomExtension2User.java and RummyRoomExtension3User.java

Setting Adapter

Setting adapter is a necessary task to override default server side functionality. First we set BaseServerAdapter at the time of starting AppWarpS2.

    String appconfigPath = System.getProperty("user.dir")+System.getProperty("file.separator")+"AppConfig.json";
    boolean started = AppWarpServer.start(new RummyServerExtension(), appconfigPath);
        if(!started){
            throw new Exception("AppWarpServer did not start. See logs for details.");
        }

we set zone adapter in RummyServerExtension.java as we receive notification onZoneCreated.

    @Override
    public void onZoneCreated(IZone zone)
    {             
        zone.setAdaptor(new RummyZoneExtension(zone));
    }

and We set BaseTurnRoomAdaptor as we receive notification on creating room in RummyZoneExtension.

    @Override
    public void handleCreateRoomRequest(IUser user, IRoom room, HandlingResult result)
    {
        if(room.isTurnBased() && room.getMaxUsers()==2){
            room.setAdaptor(new RummyRoomExtension2User(izone, (ITurnBasedRoom)room));
        }else if(room.isTurnBased() && room.getMaxUsers()==3){
            room.setAdaptor(new RummyRoomExtension3User(izone, (ITurnBasedRoom)room));
        }
        else{
            result.code = WarpResponseResultCode.BAD_REQUEST;
        }
    }
    

Custom server side authentication

To check custom-auth we have logic in BaseZoneAdapter handleAddUserRequest i.e. if user selects Login with facebook, we check on server with Facebook Graph API, if user has the same user id from client facebook oauth token.

@Override
    public void handleAddUserRequest(final IUser user, final String authData, final HandlingResult result){
        if(authData!=null && authData.length()>0){
            result.code = WarpResponseResultCode.AUTH_PENDING;// indicates that response will be sent asynchronously
            new Thread(new Runnable() {
                @Override
                public void run() {
                    checkForAuth(user, authData, result);
                }
            }).start();
        }
    }

In checkForAuth we check using facebook Graph API whether user has the same fb_id as sent in auth_data.

if(response.get("id").equals(user.getName())){
                    // "Auth success on server"
                    izone.sendAddUserResponse(user, WarpResponseResultCode.SUCCESS, "Auth success on server");
                }else{
                    // "Auth Failed on server"
                    izone.sendAddUserResponse(user, WarpResponseResultCode.AUTH_ERROR, "Auth failed on server");
                }

Starting the Game

Game is started by server if joined users in room and max users in room are same. This is done using the startGame operation API of ITurnBasedRoom.

@Override
    public void handleTimerTick(long time){
         if(GAME_STATUS==CardsConstants.STOPPED && gameRoom.getJoinedUsers().size()==gameRoom.getMaxUsers()){
                GAME_STATUS=CardsConstants.RUNNING;
                dealNewCards();
                gameRoom.startGame(CardsConstants.SERVER_NAME);
         }
    }

Handling move

When a client sends a move, the server validates it. To validate the move, server checks if the top card sent by the client is actually held by the sender or not.

@Override
    public void handleMoveRequest(IUser sender, String moveData, HandlingResult result){
            int top_card =-1;
            JSONObject data = new JSONObject(moveData);
            top_card = data.getInt("top");
            validateAndHandleMove(sender, top_card, result);
            ....
    }


_validateAndHandleMove_

if(USER_1_HAND.indexOf(topCard)!=-1){
...
}

Check For Win

When a client submits his cards, then server checks if client has won the game or not on the basis of rules(defined above). If the client has won, server declares the client as winning user and broadcasts the result. If client cards are not the winning cards then server will send back a notification to the client.

boolean status = Utils.checkForWin(cardList);
            if(status){// for winning condition
                if(sender.getName().equals(user1_name)){
                    handleFinishGame(user1_name, cardList);
                }else if(sender.getName().equals(user2_name)){
                    handleFinishGame(user2_name, cardList);
                }
            }else{
                String desc = CardsConstants.SUBMIT_CARD+"#"+"You don't have winning cards";
                sender.SendChatNotification(CardsConstants.SERVER_NAME, desc, gameRoom);
            }

Miscellaneous

TurnBasedRoom

The RummyDemo uses AppWarpS2 TurnBasedRoom API. In this room, turn time is 180 seconds. User has to complete his turn in turn time otherwise his turn will expire. By default AppWarpS2 gives turn to next user. If you want to edit this functionality you can define logic in onTurnExpired of BaseTrunRoomAdapter

public void onTurnExpired(IUser turn, HandlingResult result)
    {

    }

Handling leaving user

If the game has started and a user leaves the game then in case of a two-user game, server declares the other user as winning user, and in case of a three user game, the server continues with the game and places leaving user’s cards in deck

@Override
    public void onUserLeavingTurnRoom(IUser user, HandlingResult result){
        String leaveingUser = null;
        if(user.getName().equals(user1_name)){
            leaveingUser = user1_name;
        }else if(user.getName().equals(user2_name)){
            leaveingUser = user2_name;
        }
        String message = "You Win! Enemy "+leaveingUser+" left the room";
        gameRoom.BroadcastChat(CardsConstants.SERVER_NAME, CardsConstants.RESULT_USER_LEFT+"#"+message);
        ...
    }

Connection Resiliency

Rummy Demo client has AppWarp Connection Resiliency feature. When the server receives onUserPause then it stops the game.

@Override
    public void onUserPaused(IUser user){
        if(user.getLocation().getMaxUsers()==2){
            RummyRoomExtension2User extension = (RummyRoomExtension2User)user.getLocation().getAdaptor();
            extension.onUserPaused(user);
        }
        ....
    }

_onUserPaused_
if(gameRoom.getJoinedUsers().contains(user)){
            pausedUserList.add(user);
            GAME_STATUS = CardsConstants.PAUSED;
            gameRoom.stopGame(CardsConstants.SERVER_NAME);
        }

Server resume the game as it receives handleResumeUserRequest

@Override
    public void handleResumeUserRequest(IUser user, String authData, HandlingResult result){
        if(user.getLocation().getMaxUsers()==2){
            RummyRoomExtension2User extension = (RummyRoomExtension2User)user.getLocation().getAdaptor();
            extension.onUserResume(user);
        }
        ....
    }

_onUserResume_
if(pausedUserList.indexOf(user)!=-1){
            pausedUserList.remove(user);
        }
        if(pausedUserList.isEmpty()){
            GAME_STATUS = CardsConstants.RESUMED;
        }