/* Copyright (C) 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.if3games.chessonline.data; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.json.JSONException; import org.json.JSONObject; import com.google.android.gms.games.Games; import com.if3games.chessonline.utils.EloRatingSystem; import android.content.SharedPreferences; /** * Represents the player's progress in the game. The player's progress is how many stars * they got on each level. * * @author Bruno Oliveira */ public class SaveGame { // serialization format version private static final String SERIAL_VERSION = "9.0"; private String username; // Maps a level name (like "2-8") to the number of stars the user has in that level. // Any key that doesn't exist in this map is considered to be associated to the value 0. Map<String,Integer> mStats = new HashMap<String,Integer>(ConstantsData.initMap); /** Constructs an empty SaveGame object. No stars on no levels. */ public SaveGame() { } /** Constructs a SaveGame object from serialized data. */ public SaveGame(byte[] data) { if (data == null) return; // default progress loadFromJson(new String(data)); } /** Constructs a SaveGame object from a JSON string. */ public SaveGame(String json) { if (json == null) return; // default progress loadFromJson(json); } /** Constructs a SaveGame object by reading from a SharedPreferences. */ public SaveGame(SharedPreferences sp, String key) { loadFromJson(sp.getString(key, "")); } /** Replaces this SaveGame's content with the content loaded from the given JSON string. */ public void loadFromJson(String json) { //zero(); if (json == null || json.trim().equals("")) return; try { JSONObject obj = new JSONObject(json); String format = obj.getString("version"); if (!format.equals(SERIAL_VERSION)) { throw new RuntimeException("Unexpected loot format " + format); } JSONObject stats = obj.getJSONObject("stats"); Iterator<?> iter = stats.keys(); while (iter.hasNext()) { String statName = (String)iter.next(); mStats.put(statName, stats.getInt(statName)); } } catch (JSONException ex) { ex.printStackTrace(); throw new RuntimeException("Save data has a syntax error: " + json, ex); } catch (NumberFormatException ex) { ex.printStackTrace(); throw new RuntimeException("Save data has an invalid number in it: " + json, ex); } } /** Serializes this SaveGame to an array of bytes. */ public byte[] toBytes() { return toString().getBytes(); } /** Serializes this SaveGame to a JSON string. */ @Override public String toString() { try { JSONObject stats = new JSONObject(); for (String statName : mStats.keySet()) { stats.put(statName, mStats.get(statName)); } JSONObject obj = new JSONObject(); obj.put("version", SERIAL_VERSION); obj.put("username", username); obj.put("stats", stats); return obj.toString(); } catch (JSONException ex) { ex.printStackTrace(); throw new RuntimeException("Error converting save data to JSON.", ex); } } /** * Computes the union of this SaveGame with the given SaveGame. The union will have any * levels present in either operand. If the same level is present in both operands, * then the number of stars will be the greatest of the two. * * @param other The other operand with which to compute the union. * @return The result of the union. */ public SaveGame unionWith(SaveGame other) { SaveGame result = clone(); int tmpNewRating = 0; boolean isChangeStats = false; for (String statName : other.mStats.keySet()) { int existingStats = result.getStatsFromName(statName); int newStats = other.getStatsFromName(statName); if(statName.equals(ConstantsData.CH_KEY_RATIND)) { tmpNewRating = newStats; continue; } // only overwrite if number of stars is greater if (newStats > existingStats) { result.setStatsFromName(statName, newStats); isChangeStats = true; } // note that this code doesn't preserve mappings from a level to the value 0, // but that is not a problem because, in our semantics, the absence of a mapping // is equivalent to mapping to 0 stars. } if(isChangeStats) result.setStatsFromName(ConstantsData.CH_KEY_RATIND, tmpNewRating); return result; } /** Returns a clone of this SaveGame object. */ public SaveGame clone() { SaveGame result = new SaveGame(); for (String statName : mStats.keySet()) { result.setStatsFromName(statName, getStatsFromName(statName)); } return result; } /** Resets this SaveGame object to be empty. Empty means no stars on no levels. */ public void zero() { mStats.clear(); } /** Returns whether or not this SaveGame is empty. Empty means no stars on no levels. */ public boolean isZero() { return mStats.keySet().size() == 0; } /** Save this SaveGame object to a SharedPreferences. */ public void save(SharedPreferences sp, String key) { SharedPreferences.Editor spe = sp.edit(); spe.putString(key, toString()); spe.commit(); } public int getStatsFromName(String statName) { Integer r = mStats.get(statName); return r == null ? 0 : r.intValue(); } public void setStatsFromName(String statName, int stars) { mStats.put(statName, stars); } /** * Set username from current getApiClient (Games.Players.getCurrentPlayer(getApiClient()).getDisplayName()). * * @param username GMS player name * */ public void setUserName(String username) { this.username = username; } /** * Set stats info from result of match. * * @param result * 1=WON, 0=DRAW, -1=LOSS * @param opponentRating * Rating of either the opponent player or the average of the * opponent team or teams. * @param gameMode * This param for K Factor: gameMode=1 - Blitz mode * gameMode=0 - Standart game mode */ public void setStatsFromResult(int result, int opponentRating, int gameMode) { boolean isNewbie = mStats.get(ConstantsData.CH_KEY_PLAYED) > 30 ? false : true; EloRatingSystem rs = EloRatingSystem.getInstance("ChessOnline"); mStats.put(ConstantsData.CH_KEY_PLAYED, mStats.get(ConstantsData.CH_KEY_PLAYED) + 1); switch (result) { case ConstantsData.GAME_WON: // won int rating = rs.getNewRating(mStats.get(ConstantsData.CH_KEY_RATIND), opponentRating, ConstantsData.GAME_WON, gameMode, isNewbie); mStats.put(ConstantsData.CH_KEY_RATIND, rating); mStats.put(ConstantsData.CH_KEY_WON, mStats.get(ConstantsData.CH_KEY_WON) + 1); break; case ConstantsData.GAME_DRAW: // draw int rating1 = rs.getNewRating(mStats.get(ConstantsData.CH_KEY_RATIND), opponentRating, ConstantsData.GAME_DRAW, gameMode, isNewbie); mStats.put(ConstantsData.CH_KEY_RATIND, rating1); mStats.put(ConstantsData.CH_KEY_DRAW, mStats.get(ConstantsData.CH_KEY_DRAW) + 1); break; case ConstantsData.GAME_LOSS: // lost int rating2 = rs.getNewRating(mStats.get(ConstantsData.CH_KEY_RATIND), opponentRating, ConstantsData.GAME_LOSS, gameMode, isNewbie); mStats.put(ConstantsData.CH_KEY_RATIND, rating2); mStats.put(ConstantsData.CH_KEY_LOST, mStats.get(ConstantsData.CH_KEY_LOST) + 1); break; default: break; } } }