From fedb7563d9899740cef0947aa2e46b6b965fcdb1 Mon Sep 17 00:00:00 2001 From: Ruben Date: Sat, 10 Sep 2016 00:03:10 +0100 Subject: [PATCH] Add a bonus culture --- .../emotionhero/ApiRestClient.java | 2 + .../com/rubenvandeven/emotionhero/Game.java | 17 +++++- .../emotionhero/GameOpenHelper.java | 30 +++++++--- .../emotionhero/HighscoreActivity.java | 2 +- .../com/rubenvandeven/emotionhero/Hit.java | 55 ++++++++++++++++--- .../emotionhero/ReviewActivity.java | 2 +- .../rubenvandeven/emotionhero/Scenario.java | 22 ++++++-- .../emotionhero/ScenarioView.java | 51 ++++++++++++++++- 8 files changed, 154 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/ApiRestClient.java b/app/src/main/java/com/rubenvandeven/emotionhero/ApiRestClient.java index c02b953..ef1c4cb 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/ApiRestClient.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/ApiRestClient.java @@ -174,6 +174,7 @@ public class ApiRestClient { try { j.put("lvl_id", game.scenario.id); j.put("score", game.score); + j.put("bonus", game.bonus); TimeZone tz = TimeZone.getTimeZone("UTC"); DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); df.setTimeZone(tz); @@ -187,6 +188,7 @@ public class ApiRestClient { jHit.put("id", hit.id); jHit.put("target_index", hit.target.index); jHit.put("score", hit.score); + jHit.put("bonus", hit.bonus); jHit.put("glasses", hit.glasses); jHit.put("ethnicity", hit.ethnicity); jHit.put("age", hit.age); diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/Game.java b/app/src/main/java/com/rubenvandeven/emotionhero/Game.java index d9a0fe2..1e59208 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/Game.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/Game.java @@ -18,18 +18,20 @@ public class Game { public Long id; public Scenario scenario; public float score; + public float bonus; public Date time; public String remoteId; public Map hits = new HashMap<>(); - public Game(Long id, int lvl_id, float score, Date time, String remoteId) { - this(id, new Scenario(lvl_id, null), score, time, remoteId); + public Game(Long id, int lvl_id, float score, float bonus, Date time, String remoteId) { + this(id, new Scenario(lvl_id, null), score, bonus, time, remoteId); } - public Game(Long id, Scenario scenario, float score, Date time, String remoteId) { + public Game(Long id, Scenario scenario, float score, float bonus, Date time, String remoteId) { this.id = id; this.scenario= scenario; this.score = score; + this.bonus = bonus; this.time = time == null ? new Date() : time; this.remoteId = remoteId; } @@ -37,6 +39,7 @@ public class Game { public void addHit(Hit hit) { hits.put(hit.target.index, hit); score = calculateScore(); + bonus = calculateBonus(); } private float calculateScore() { @@ -46,4 +49,12 @@ public class Game { } return s; } + + private float calculateBonus() { + float s = 0; + for(Hit hit: hits.values()) { + s += hit.bonus; + } + return s; + } } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/GameOpenHelper.java b/app/src/main/java/com/rubenvandeven/emotionhero/GameOpenHelper.java index 836557b..eaf680a 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/GameOpenHelper.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/GameOpenHelper.java @@ -20,13 +20,14 @@ import java.util.Date; * adb pull /sdcard/emotionhero.db */ public class GameOpenHelper extends SQLiteOpenHelper { - private static final int DATABASE_VERSION = 3; + private static final int DATABASE_VERSION = 4; private static final String GAME_TABLE_NAME = "games"; private static final String GAME_TABLE_CREATE = "CREATE TABLE " + GAME_TABLE_NAME + " (" + "id INTEGER NOT NULL," + "lvl_id INTEGER NOT NULL," + "score VARCHAR(255) NOT NULL," + + "bonus VARCHAR(255) NOT NULL," + "time INTEGER NOT NULL," + "remoteId VARCHAR(255) DEFAULT NULL, " + "PRIMARY KEY(id));"; @@ -34,6 +35,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { "id", "lvl_id", "score", + "bonus", "time", "remoteId" }; @@ -45,6 +47,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { "game_id INTEGER NOT NULL," + "target_index INTEGER NOT_NULL," + "score VARCHAR(20)," + + "bonus VARCHAR(20)," + "remoteId VARCHAR(20)," + "glasses VARCHAR(20)," + "ethnicity VARCHAR(20)," + @@ -149,7 +152,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { "image BLOB," + "PRIMARY KEY (id));" ; private static final String[] HIT_PROJECTION = { - "id","game_id", "target_index","score","remoteId","glasses","ethnicity","age","gender","anger","contempt","disgust","fear","joy","sadness","surprise","roll","pitch","yaw","inter_ocular_distance","mouth_open","lip_press","brow_raise","nose_wrinkler","lip_depressor","brow_furrow","attention","smile","inner_brow_raiser","chin_raiser","smirk","lip_suck","upper_lip_raiser","lip_pucker","eye_closure","engagement","valence","point_0x","point_0y","point_1x","point_1y","point_2x","point_2y","point_3x","point_3y","point_4x","point_4y","point_5x","point_5y","point_6x","point_6y","point_7x","point_7y","point_8x","point_8y","point_9x","point_9y","point_10x","point_10y","point_11x","point_11y","point_12x","point_12y","point_13x","point_13y","point_14x","point_14y","point_15x","point_15y","point_16x","point_16y","point_17x","point_17y","point_18x","point_18y","point_19x","point_19y","point_20x","point_20y","point_21x","point_21y","point_22x","point_22y","point_23x","point_23y","point_24x","point_24y","point_25x","point_25y","point_26x","point_26y","point_27x","point_27y","point_28x","point_28y","point_29x","point_29y","point_30x","point_30y","point_31x","point_31y","point_32x","point_32y","point_33x","point_33y", "image" + "id","game_id", "target_index","score","bonus","remoteId","glasses","ethnicity","age","gender","anger","contempt","disgust","fear","joy","sadness","surprise","roll","pitch","yaw","inter_ocular_distance","mouth_open","lip_press","brow_raise","nose_wrinkler","lip_depressor","brow_furrow","attention","smile","inner_brow_raiser","chin_raiser","smirk","lip_suck","upper_lip_raiser","lip_pucker","eye_closure","engagement","valence","point_0x","point_0y","point_1x","point_1y","point_2x","point_2y","point_3x","point_3y","point_4x","point_4y","point_5x","point_5y","point_6x","point_6y","point_7x","point_7y","point_8x","point_8y","point_9x","point_9y","point_10x","point_10y","point_11x","point_11y","point_12x","point_12y","point_13x","point_13y","point_14x","point_14y","point_15x","point_15y","point_16x","point_16y","point_17x","point_17y","point_18x","point_18y","point_19x","point_19y","point_20x","point_20y","point_21x","point_21y","point_22x","point_22y","point_23x","point_23y","point_24x","point_24y","point_25x","point_25y","point_26x","point_26y","point_27x","point_27y","point_28x","point_28y","point_29x","point_29y","point_30x","point_30y","point_31x","point_31y","point_32x","point_32y","point_33x","point_33y", "image" }; private Context context; @@ -179,6 +182,10 @@ public class GameOpenHelper extends SQLiteOpenHelper { if(oldVersion == 2 && newVersion == 3) { db.execSQL("ALTER TABLE hits ADD image BLOB;"); } + if(oldVersion == 3 && newVersion == 4) { + db.execSQL("ALTER TABLE hits ADD bonus VARCHAR(20);"); + db.execSQL("ALTER TABLE games ADD bonus VARCHAR(255);"); + } } public void insertGame(Game game) { @@ -189,6 +196,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { ContentValues values = new ContentValues(); values.put("lvl_id", game.scenario.id); values.put("score", game.score); + values.put("bonus", game.bonus); values.put("time", game.time.getTime()); if(game.remoteId == null) { values.putNull("remoteId"); @@ -216,7 +224,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { String selection = "lvl_id = ?"; String[] selectionArgs = { Integer.toString(s.id) }; - String sortOrder = "score DESC"; + String sortOrder = "score+bonus DESC"; Cursor c = db.query( GAME_TABLE_NAME, // The table to query @@ -237,8 +245,15 @@ public class GameOpenHelper extends SQLiteOpenHelper { c.moveToFirst(); while (!c.isAfterLast()) { - Scenario scenario = s == null ? new Scenario(c.getInt(1), null) : s; - Game game = new Game(c.getLong(0), scenario, c.getFloat(2), new Date(c.getLong(3)), c.getString(4)); + Scenario scenario = s == null ? new Scenario(c.getInt(c.getColumnIndex("lvl_id")), null) : s; + Game game = new Game( + c.getLong(c.getColumnIndex("id")), + scenario, + c.getFloat(c.getColumnIndex("score")), + c.getFloat(c.getColumnIndex("bonus")), + new Date(c.getLong(c.getColumnIndex("time"))), + c.getString(c.getColumnIndex("remoteId")) + ); games[i] = game; this.getHitsForGame(game); // hits are appended to game object i++; @@ -277,6 +292,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { values.put("game_id", hit.game.id); values.put("target_index", hit.target.index); values.put("score", hit.score); + values.put("bonus", hit.bonus); values.put("glasses", hit.glasses); values.put("ethnicity", hit.ethnicity); @@ -389,10 +405,10 @@ public class GameOpenHelper extends SQLiteOpenHelper { } public int getLocalRankOfGame(Game game) { - String[] params = { Long.toString(game.scenario.id), Double.toString(game.score) }; + String[] params = { Long.toString(game.scenario.id), Double.toString(game.score + game.bonus) }; Log.e("GAME",params[0].toString()); Log.e("GAME",params[1].toString()); - Cursor c = getReadableDatabase().rawQuery("SELECT COUNT(id)+1 FROM games WHERE lvl_id = ? AND score > ?", params); + Cursor c = getReadableDatabase().rawQuery("SELECT COUNT(id)+1 FROM games WHERE lvl_id = ? AND score+bonus > ?", params); c.moveToFirst(); return c.getInt(0); } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/HighscoreActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/HighscoreActivity.java index b2c5b58..6f63fd4 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/HighscoreActivity.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/HighscoreActivity.java @@ -217,7 +217,7 @@ public class HighscoreActivity extends AppCompatActivity { for(Game game: games) { final long itemGameId = game.id; i++; - String highscoreText = String.format("%1$d. %2$.4f", i, game.score); //make line by line elements + String highscoreText = String.format("%1$d. %2$.4f", i, game.score+game.bonus); //make line by line elements TextView scoreItem = new TextView(getContext()); scoreItem.setText(highscoreText); scoreItem.setTextColor(ContextCompat.getColor(getContext(), R.color.highscore)); diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/Hit.java b/app/src/main/java/com/rubenvandeven/emotionhero/Hit.java index 4176aab..94e27eb 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/Hit.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/Hit.java @@ -6,6 +6,7 @@ import android.graphics.BitmapFactory; import android.graphics.PointF; import android.graphics.RectF; import android.util.Base64; +import android.util.Log; import com.affectiva.android.affdex.sdk.detector.Face; import com.google.gson.annotations.Expose; @@ -29,6 +30,7 @@ public class Hit { public Game game; public float score; + public float bonus; public Map emotions = new HashMap<>(7); @@ -48,8 +50,7 @@ public class Hit { /** * Constructor for game moment */ - public Hit(Scenario.Target target, Game game, Face face, float score, Bitmap frame) { - this.score = score; + public Hit(Scenario.Target target, Game game, Face face) { this.target = target; this.game = game; for(Emotion emotion: Emotion.values()) { @@ -67,7 +68,10 @@ public class Hit { this.points = face.getFacePoints().clone(); - this.frame = frame; +// this.frame = frame; + + this.score = calculateScore(); + this.bonus = calculateBonus(); // this in a way is strange behaviour: // preferably do the game.addHit after creating hit, and call hit.game = game from there. @@ -80,6 +84,7 @@ public class Hit { public Hit(Game game, Cursor c) { this.id = c.getLong(c.getColumnIndex("id")); this.score = c.getFloat(c.getColumnIndex("score")); + this.bonus = c.getFloat(c.getColumnIndex("bonus")); String rId = c.getString(c.getColumnIndex("remoteId")); this.remoteId = (rId == null || rId.length() == 0) ? null : c.getString(c.getColumnIndex("remoteId")); this.target = game.scenario.getTargetByIndex(c.getInt(c.getColumnIndex("target_index"))); @@ -136,11 +141,12 @@ public class Hit { this.points[32] = new PointF(c.getFloat(c.getColumnIndex("point_32x")), c.getFloat(c.getColumnIndex("point_32y"))); this.points[33] = new PointF(c.getFloat(c.getColumnIndex("point_33x")), c.getFloat(c.getColumnIndex("point_33y"))); - byte[] image = c.getBlob(c.getColumnIndex("image")); - if(image != null) - { - frame = BitmapFactory.decodeByteArray(image, 0, image.length); - } +// skip for now, unused and bad for performance ;-) +// byte[] image = c.getBlob(c.getColumnIndex("image")); +// if(image != null) +// { +// frame = BitmapFactory.decodeByteArray(image, 0, image.length); +// } game.addHit(this); } @@ -184,4 +190,37 @@ public class Hit { frame.compress(Bitmap.CompressFormat.JPEG, 90, stream); return stream.toByteArray(); } + + public float calculateScore() { + float scored_value = emotions.get(target.emotion); + float required_value = target.value; + float diff = Math.abs(scored_value-required_value); + return 100 - diff; + + } + + public float calculateBonus() { + float scored_value = emotions.get(target.emotion); + float required_value = target.value; + float diff = Math.abs(scored_value-required_value); + + float bonus = 0; + // precision score + if(diff < 25) { + bonus = ((25-diff)/25) * 50; + } + + // double score if its exact!! + if(diff < 0.001) { + bonus += 50; + } + + Log.e("HIT", "bonus: " + bonus + " (diff "+diff+")"); + + return bonus; + } + + public boolean hasPrecisionBonus() { + return (this.bonus > 0); + } } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/ReviewActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/ReviewActivity.java index 7f1941d..865903c 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/ReviewActivity.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/ReviewActivity.java @@ -160,7 +160,7 @@ public class ReviewActivity extends AppCompatActivity { lvlNameText.setText("\""+game.scenario.toString()+"\""); PrettyTime p = new PrettyTime(); dateText.setText(p.format(game.time)); - scoreText.setText(String.format("%1$.4f", game.score)); + scoreText.setText(String.format("%1$.4f", game.score+game.bonus)); // overallScorePercText.setText(String.format("%1$.0f%%", 30f)); loadRemoteInfo.run(); diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/Scenario.java b/app/src/main/java/com/rubenvandeven/emotionhero/Scenario.java index e6d5df0..bcee462 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/Scenario.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/Scenario.java @@ -136,7 +136,7 @@ public class Scenario { } }; timer.schedule(tickTask, 0, 1000/DESIRED_FPS); - this.game = new Game(null, this, 0, new Date(), null); + this.game = new Game(null, this, 0, 0, new Date(), null); rs = RenderScript.create(_activity); squarePaint = new Paint(); @@ -164,12 +164,10 @@ public class Scenario { continue; } if(target.timestamp <= runningTime) { - float scored_value = target.emotion.getValueFromFace(currentFace); - float required_value = target.value; - float score = 100 - Math.abs(scored_value-required_value); + Hit hit = new Hit(target, game, currentFace); _activity.sound.play(_activity.soundIds.get(_activity.SOUND_SCORE), 1, 1, - 1,0, score / 200f + 0.5f ); // play back the sound slower + 1,0, hit.score / 200f + 0.5f ); // play back the sound slower // depending on hit value target.isHit = true; @@ -198,7 +196,7 @@ public class Scenario { */ } - Hit hit = new Hit(target, game, currentFace, score, outputBitmap); + hit.frame = outputBitmap; } } @@ -264,10 +262,22 @@ public class Scenario { } return value; } + public float getBonusTotalValue() + { + float value = 0; + for (Hit hit : game.hits.values()) { + value += hit.bonus; + } + return value; + } public float getHitPercentage() { return (getHitTotalValue()/maxScore) * 100; } + public float getBonusPercentage() { + // maxScore for bonus == normal maxScore + return (getBonusTotalValue()/maxScore) * 100; + } public float getMissedPercentage() { return (getHitTotalMisValue()/maxScore) * 100; diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java b/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java index 1674c0b..f23f9a9 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java @@ -3,9 +3,11 @@ package com.rubenvandeven.emotionhero; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.Typeface; import android.util.Log; @@ -43,6 +45,13 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback private Paint linePaint = new Paint(); private Paint scorePaint = new Paint(); private Paint scoreFinishedPaint = new Paint(); + private Paint bonusBarPaint = new Paint(); + + private Point mLeftTop; + private Point mRightTop; + private Point mLeftBot; + private Point mRightBot; + private Matrix matrix; public boolean drawOverlay = false; @@ -97,6 +106,8 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback scorePaint.setTextSize(50); scorePaint.setTypeface(Typeface.DEFAULT_BOLD); + bonusBarPaint.setColor(Color.rgb(132, 0, 255)); + scoreFinishedPaint.setColor(Color.YELLOW); scoreFinishedPaint.setTextSize(100); scoreFinishedPaint.setTypeface(Typeface.DEFAULT_BOLD); @@ -116,12 +127,14 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback Paint emoPaint = new Paint(); emoPaint.setTextSize(22); emoPaint.setColor(emotion.getColor()); +// emoPaint.setShadowLayer(10,0,0,Color.BLACK); emoPaints.put(emotion, emoPaint); Paint emoPaintOutline = new Paint(); emoPaintOutline.setColor(emotion.getColor()); emoPaintOutline.setStyle(Paint.Style.STROKE); emoPaintOutline.setStrokeWidth(3); +// emoPaintOutline.setShadowLayer(10,0,0,Color.BLACK); emoOutlinePaints.put(emotion, emoPaintOutline); Paint emoScoredPaint = new Paint(); @@ -136,8 +149,13 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback emoScoredPaint.setTextSize(20); emoScoredPaint.setStrokeWidth(2); emoScoredPaint.setStyle(Paint.Style.FILL_AND_STROKE); +// emoScoredPaint.setShadowLayer(10,0,0,Color.BLACK); emoScoredPaints.put(emotion, emoScoredPaint); + setLayerType(LAYER_TYPE_SOFTWARE, emoPaint); + setLayerType(LAYER_TYPE_SOFTWARE, emoPaintOutline); + setLayerType(LAYER_TYPE_SOFTWARE, emoScoredPaint); + } } @@ -147,6 +165,12 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback if(drawOverlay) { canvas.drawColor(0x990000FF); } + + if(matrix != null) { + canvas.concat(matrix); + } + + //do drawing stuff here. if (noFace) @@ -215,6 +239,10 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback // Paint targetPaint = (target.hit == null) ? emoPaints.get(target.emotion) : emoScoredPaints.get(target.emotion); float cy = bottomline_height - (target.timestamp * sec_height) + diff_y; + if(cy < (-1 * canvas.getHeight()) ) { + continue; + } + float cx = padding_left + (step_y * target.emotion.ordinal() + step_y / 2); canvas.drawCircle(cx, cy, max_ball_radius * target.value/100, emoPaints.get(target.emotion)); @@ -241,20 +269,24 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback float hitWidth = width*(_scenario.getHitPercentage()/100); float misWidth = width*(_scenario.getMissedPercentage()/100); + float bonusWidth = width*(_scenario.getBonusPercentage()/100); String scoreText = String.format("%1$.0f", _scenario.getHitTotalValue()); Rect scoreTextBounds = new Rect(); scorePaint.getTextBounds(scoreText, 0, scoreText.length(), scoreTextBounds); canvas.drawText(scoreText, hitWidth, height*0.95f-scoreTextBounds.height()-5, scorePaint); + canvas.drawRect(0, height*0.95f, width, height, mainPaint); float stepSize = width / _scenario.targets.size(); for(int i = _scenario.targets.size(); i > 0; i--) { canvas.drawLine(stepSize*i, height*0.95f, stepSize*i, height, attrScoreLinePaint); } - canvas.drawRect(0, height*0.95f, hitWidth, height, emoPaints.get(Emotion.JOY)); + canvas.drawRect(0, height*0.95f, hitWidth, height, emoPaints.get(Emotion.JOY)); // paint: yellow canvas.drawRect(hitWidth, height*0.95f, hitWidth+misWidth, height, attrScorePaint); + canvas.drawRect(0, height*0.95f, bonusWidth, height*0.975f, bonusBarPaint); + } @@ -271,7 +303,24 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + // canvas has exact same size as surface :-) + mLeftTop = new Point((int) (width*0.2), height/2); + mRightTop = new Point((int) (width*0.8), height/2); + mLeftBot = new Point(0, height); + mRightBot = new Point(width, height); + matrix = new Matrix(); + matrix.setPolyToPoly(new float[]{0, 0, + width - 1, 0, + 0, height - 1, + width - 1, height - 1}, + 0, + new float[]{mLeftTop.x, mLeftTop.y, + mRightTop.x, mRightTop.y, + mLeftBot.x, mLeftBot.y, + mRightBot.x, mRightBot.y + } + , 0, 4); } @Override