1 package de
.cwde
.shisensho
;
4 import java
.util
.Locale
;
5 import java
.util
.Timer
;
6 import java
.util
.TimerTask
;
8 import android
.app
.Activity
;
9 import android
.content
.Context
;
10 import android
.graphics
.Bitmap
;
11 import android
.graphics
.BitmapFactory
;
12 import android
.graphics
.Canvas
;
13 import android
.graphics
.Color
;
14 import android
.graphics
.Matrix
;
15 import android
.graphics
.Paint
;
16 import android
.graphics
.Paint
.Align
;
17 import android
.graphics
.Paint
.Cap
;
18 import android
.graphics
.Paint
.Join
;
19 import android
.graphics
.Paint
.Style
;
20 import android
.graphics
.Rect
;
21 import android
.graphics
.Typeface
;
22 import android
.os
.Handler
;
23 import android
.os
.Message
;
24 import android
.view
.MenuItem
;
25 import android
.view
.MotionEvent
;
26 import android
.view
.SurfaceHolder
;
27 import android
.view
.SurfaceView
;
29 class ShisenShoView
extends SurfaceView
implements SurfaceHolder
.Callback
{
31 private enum StatePlay
{ UNINITIALIZED
, IDLE
, SELECTED1
, SELECTED2
, GAMEOVER
};
32 private enum StatePaint
{ BOARD
, SELECTED1
, SELECTED2
, MATCHED
, WIN
, LOSE
, HINT
, TIME
};
34 private int screenWidth
;
35 private int screenHeight
;
36 private int tilesetRows
;
37 private int tilesetCols
;
38 private int tileHeight
;
39 private int tileWidth
;
41 private Bitmap tile
[];
42 private int[] selection1
=new int[2];
43 private int[] selection2
=new int[2];
44 private List
<Point
> path
=null;
45 private List
<Line
> pairs
=null;
46 private long startTime
;
47 private long playTime
;
48 private long baseTime
;
50 private static Handler timerHandler
;
52 private boolean timerRegistered
=false;
53 private ShisenSho app
;
54 private StatePlay cstate
;
55 private StatePaint pstate
;
56 private Canvas canvas
= null;
57 private SurfaceHolder surfaceHolder
= null;
58 public ShisenShoView(ShisenSho shishenSho
) {
59 super((Context
)shishenSho
);
60 this.app
= shishenSho
;
61 cstate
= StatePlay
.UNINITIALIZED
;
62 surfaceHolder
= getHolder();
63 surfaceHolder
.addCallback(this);
66 public ShisenShoView(Context ctx
) {
71 private void paint(StatePaint pstate
) {
76 private void control(StatePlay cstate
) {
80 private void loadTileset() {
81 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
83 Bitmap tileset
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.tileset
, ops
);
84 tileset
.setDensity(Bitmap
.DENSITY_NONE
);
86 // The tile set has 4 rows x 9 columns
89 tileWidth
= tileset
.getWidth()/tilesetCols
;
90 tileHeight
= tileset
.getHeight()/tilesetRows
;
91 tile
= new Bitmap
[tilesetRows
*tilesetCols
];
94 Matrix matrix
= new Matrix();
95 matrix
.setScale(1.0f
, 1.0f
); // FIXME!
100 for (int i
=0; i
<tilesetRows
; i
++) {
101 for (int j
=0; j
<tilesetCols
; j
++) {
102 tile
[k
] = Bitmap
.createBitmap(tileset
, j
*tileWidth
, i
*tileHeight
, tileWidth
, tileHeight
, matrix
, false);
103 tile
[k
].setDensity(Bitmap
.DENSITY_NONE
);
107 tileWidth
= tile
[0].getWidth();
108 tileHeight
= tile
[0].getHeight();
111 private void loadBackground() {
112 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
113 ops
.inScaled
= false;
114 bg
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.kshisen_bgnd
, ops
);
115 bg
.setDensity(Bitmap
.DENSITY_NONE
);
118 private void registerTimer() {
119 if (timer
!=null) return; // Already registered
120 timerHandler
= new Handler() {
121 public void handleMessage(Message msg
) {
126 timer
.scheduleAtFixedRate(new TimerTask() {
128 timerHandler
.sendEmptyMessage(Activity
.RESULT_OK
);
131 timerRegistered
=true;
134 private void unregisterTimer() {
135 if (timer
==null) return; // Already unregistered
139 timerRegistered
=false;
142 public void pauseTime() {
145 startTime
= System
.currentTimeMillis();
149 public void resumeTime() {
150 startTime
= System
.currentTimeMillis();
154 private void updateTime() {
155 if (cstate
!=StatePlay
.GAMEOVER
) {
156 playTime
= (System
.currentTimeMillis()-startTime
)/1000+baseTime
;
160 private void initializeGame() {
162 screenWidth
=getWidth();
163 screenHeight
=getHeight();
165 //undo.sensitive=false;
166 pstate
=StatePaint
.BOARD
;
168 control(StatePlay
.IDLE
);
169 startTime
=System
.currentTimeMillis();
172 if (app
.timeCounter
&& !timerRegistered
) {
175 pairs
=app
.board
.getPairs(1);
178 public boolean onOptionsItemSelected(MenuItem item
) {
179 // Handle item selection
180 switch (item
.getItemId()) {
182 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
185 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
188 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
199 public void reset() {
200 control(StatePlay
.UNINITIALIZED
);
201 paint(StatePaint
.BOARD
);
204 private void onHintActivate() {
205 if (cstate
!=StatePlay
.GAMEOVER
) {
206 pairs
=app
.board
.getPairs(1);
207 paint(StatePaint
.HINT
);
209 paint(StatePaint
.BOARD
);
210 control(StatePlay
.IDLE
);
214 private void onUndoActivate() {
215 if (app
.board
.getCanUndo()) {
216 if (cstate
==StatePlay
.GAMEOVER
&& app
.timeCounter
&& !timerRegistered
) {
217 // Reprogram the time update that had been
218 // deactivated with the game over status
222 paint(StatePaint
.BOARD
);
223 //undo.sensitive=app.board.getCanUndo();
224 control(StatePlay
.IDLE
);
228 public void onTimeCounterActivate() {
229 if (app
.timeCounter
&& cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
230 // Reprogram the time update that had been
231 // deactivated with the time_counter=false
236 private void onUpdateTime() {
238 if (!(app
.timeCounter
&& cstate
!=StatePlay
.GAMEOVER
)) {
243 @SuppressWarnings("deprecation")
244 public void drawMessage(Canvas canvas
, int x
, int y
, boolean centered
, String message
, String color
, float textSize
) {
245 Paint paint
= new Paint();
246 paint
.setColor(Color
.parseColor(color
));
247 paint
.setLinearText(true);
248 paint
.setAntiAlias(true);
249 paint
.setTextAlign(centered?Align
.CENTER
:Align
.LEFT
);
250 paint
.setTypeface(Typeface
.SANS_SERIF
);
251 paint
.setFakeBoldText(true);
252 paint
.setTextSize(textSize
);
253 canvas
.drawText(message
, x
, y
, paint
);
256 public void repaint() {
257 if (surfaceHolder
== null) return;
259 if (canvas
== null) canvas
= surfaceHolder
.lockCanvas(null);
260 if (canvas
== null) return;
261 if (cstate
==StatePlay
.UNINITIALIZED
) initializeGame();
262 synchronized (surfaceHolder
) {
266 if (canvas
!= null) {
267 surfaceHolder
.unlockCanvasAndPost(canvas
);
273 protected void doDraw(Canvas canvas
) {
276 // Bitmap buffer = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
277 //Canvas cbuffer = new Canvas(buffer);
278 Canvas cbuffer
= canvas
;
279 if (canvas
== null) return;
281 //super.onDraw(canvas);
283 // Board upper left corner on screen
287 if (app
!=null && app
.board
!=null) {
288 x0
=(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2;
289 y0
=(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2;
292 int red
= Color
.parseColor("#FF0000");
293 int orange
= Color
.parseColor("#F0C000");
294 Paint paint
= new Paint();
295 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
297 // Background & board painting
307 // Background painting
308 int bgWidth
= bg
.getWidth();
309 int bgHeight
= bg
.getHeight();
310 for (int i
=0; i
<screenHeight
/bgHeight
+1; i
++) {
311 for (int j
=0; j
<screenWidth
/bgWidth
+1; j
++) {
312 cbuffer
.drawBitmap(bg
, j
*bgWidth
, i
*bgHeight
, paint
);
317 // Max visible size: 7x17
318 if (app
!=null && app
.board
!=null) {
319 for (int i
=0;i
<app
.board
.boardSize
[0];i
++) {
320 for (int j
=0;j
<app
.board
.boardSize
[1];j
++) {
321 // Tiles are 56px height, 40px width each
322 char piece
=app
.board
.board
[i
][j
];
324 cbuffer
.drawBitmap(tile
[piece
], x0
+j
*tileWidth
, y0
+i
*tileHeight
, paint
);
332 // Red rectangle for selection 1
338 paint
.setStyle(Style
.STROKE
);
339 paint
.setStrokeCap(Cap
.ROUND
);
340 paint
.setStrokeJoin(Join
.ROUND
);
341 paint
.setStrokeWidth(3);
342 cbuffer
.drawRect(new Rect(
343 x0
+selection1
[1]*tileWidth
-2,
344 y0
+selection1
[0]*tileHeight
-2,
345 x0
+selection1
[1]*tileWidth
-2+tileWidth
+2*2,
346 y0
+selection1
[0]*tileHeight
-2+tileHeight
+2*2),
351 // Red rectangle for selection 2
356 paint
.setStyle(Style
.STROKE
);
357 paint
.setStrokeCap(Cap
.ROUND
);
358 paint
.setStrokeJoin(Join
.ROUND
);
359 paint
.setStrokeWidth(3);
360 cbuffer
.drawRect(new Rect(
361 x0
+selection2
[1]*tileWidth
-2,
362 y0
+selection2
[0]*tileHeight
-2,
363 x0
+selection2
[1]*tileWidth
-2+tileWidth
+2*2,
364 y0
+selection2
[0]*tileHeight
-2+tileHeight
+2*2),
373 paint
.setStyle(Style
.STROKE
);
374 paint
.setStrokeCap(Cap
.ROUND
);
375 paint
.setStrokeJoin(Join
.ROUND
);
376 paint
.setStrokeWidth(3);
380 for (Point p1
: path
) {
383 x0
+p0
.j
*tileWidth
-2+(tileWidth
/2),
384 y0
+p0
.i
*tileHeight
-2+(tileHeight
/2),
385 x0
+p1
.j
*tileWidth
-2+(tileWidth
/2),
386 y0
+p1
.i
*tileHeight
-2+(tileHeight
/2),
395 // Orange hint rectangles
398 if (pairs
!=null && pairs
.size()>0) {
399 Line pair
=pairs
.get(0);
402 path
=app
.board
.getPath(a
,b
);
403 paint
.setColor(orange
);
404 paint
.setStyle(Style
.STROKE
);
405 paint
.setStrokeCap(Cap
.ROUND
);
406 paint
.setStrokeJoin(Join
.ROUND
);
407 paint
.setStrokeWidth(3);
409 cbuffer
.drawRect(new Rect(
412 x0
+a
.j
*tileWidth
-2+tileWidth
+2*2,
413 y0
+a
.i
*tileHeight
-2+tileHeight
+2*2),
418 for (Point p1
: path
) {
421 x0
+p0
.j
*tileWidth
-2+(tileWidth
/2),
422 y0
+p0
.i
*tileHeight
-2+(tileHeight
/2),
423 x0
+p1
.j
*tileWidth
-2+(tileWidth
/2),
424 y0
+p1
.i
*tileHeight
-2+(tileHeight
/2),
432 cbuffer
.drawRect(new Rect(
435 x0
+b
.j
*tileWidth
-2+tileWidth
+2*2,
436 y0
+b
.i
*tileHeight
-2+tileHeight
+2*2),
442 // Win & loose notifications
445 drawMessage(cbuffer
, screenWidth
/2,screenHeight
/2,true,"You Win!", "#FFFFFF", 100);
448 drawMessage(cbuffer
, screenWidth
/2,screenHeight
/2,true,"Game Over", "#FFFFFF", 100);
452 if (app
.timeCounter
) switch (pstate
) {
462 int hours
=(int)(playTime
/(60*60));
463 int minutes
=(int)((playTime
/60)%60);
464 int seconds
=(int)(playTime
%60);
465 String time
=String
.format(Locale
.US
, "%01d:%02d:%02d", hours
, minutes
, seconds
);
467 int timePosX
=screenWidth
-120;
468 int timePosY
=screenHeight
-10;
470 drawMessage(cbuffer
, timePosX
+1,timePosY
+1,false,time
,"#000000",30);
471 drawMessage(cbuffer
, timePosX
,timePosY
,false,time
,"#FFFFFF",30);
477 debugMessage="StatePlay: "+cstate+"\n"+"StatePaint: "+pstate;
478 if (debugMessage!=null && debugMessage.length()>0) {
480 String lines[] = debugMessage.split("\n");
481 for (int i=0; i<lines.length; i++) {
482 drawMessage(cbuffer,1,l,false,lines[i],"#FFFF00",30);
488 // Double buffer dumping
489 // canvas.drawBitmap(buffer, 0, 0, null);
491 } catch (Exception e
) {
498 public boolean onTouchEvent(MotionEvent event
) {
499 if (event
.getAction()==MotionEvent
.ACTION_DOWN
) {
500 onClick(Math
.round(event
.getX()),Math
.round(event
.getY()));
502 return super.onTouchEvent(event
);
505 private void onClick(int x
, int y
) {
507 int i
=(y
-(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2)/tileHeight
;
508 int j
=(x
-(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2)/tileWidth
;
513 i
<app
.board
.boardSize
[0] &&
514 j
>=0 && j
<app
.board
.boardSize
[1] &&
515 app
.board
.board
[i
][j
]!=0) {
518 paint(StatePaint
.SELECTED1
);
519 control(StatePlay
.SELECTED1
);
523 if (i
>=0 && i
<app
.board
.boardSize
[0] &&
524 j
>=0 && j
<app
.board
.boardSize
[1] &&
525 app
.board
.board
[i
][j
]!=0) {
526 if (i
==selection1
[0] && j
==selection1
[1]) {
527 paint(StatePaint
.BOARD
);
528 control(StatePlay
.IDLE
);
532 paint(StatePaint
.SELECTED2
);
534 Point a
=new Point(selection1
[0],selection1
[1]);
535 Point b
=new Point(selection2
[0],selection2
[1]);
536 path
=app
.board
.getPath(a
,b
);
537 paint(StatePaint
.MATCHED
);
539 paint(StatePaint
.BOARD
);
544 paint(StatePaint
.BOARD
);
546 pairs
=app
.board
.getPairs(1);
547 if (pairs
.size()==0) {
548 if (app
.board
.getNumPieces()==0) {
549 paint(StatePaint
.WIN
);
551 paint(StatePaint
.LOSE
);
553 control(StatePlay
.GAMEOVER
);
555 control(StatePlay
.IDLE
);
557 //undo.sensitive=app.board.getCanUndo();
563 paint(StatePaint
.BOARD
);
566 } catch (Exception e
) {
571 public void surfaceChanged(SurfaceHolder holder
, int format
, int width
,
573 surfaceHolder
= holder
;
574 if (cstate
!=StatePlay
.GAMEOVER
&& app
.timeCounter
&& !timerRegistered
) {
580 public void surfaceCreated(SurfaceHolder holder
) {
581 surfaceHolder
= holder
;
585 public void surfaceDestroyed(SurfaceHolder holder
) {
586 surfaceHolder
= null;
587 if (timerRegistered
) {
594 protected void onDraw(Canvas canvas) {
595 super.onDraw(canvas);
597 if (!initialized) initialize();
599 long currTime = System.currentTimeMillis();
601 a = (float)(currTime - startTime) / (float)duration;
602 if (a > (float)1.0) a = (float)1.0;
604 x = Math.round(nextx*a + prevx*(1-a));
605 y = Math.round(nexty*a + prevy*(1-a));
607 if (a == (float)1.0) computeNextTarget();
609 int bgWidth = bg.getWidth();
610 int bgHeight = bg.getHeight();
611 for (int i=0; i<height/bgHeight+1; i++) {
612 for (int j=0; j<width/bgWidth+1; j++) {
613 canvas.drawBitmap(bg, j*bgWidth, i*bgHeight, paint);
617 canvas.drawBitmap(tile[randomtile], x, y, paint);
623 public boolean onTouchEvent(MotionEvent event) {
624 if (event.getActionMasked()==MotionEvent.ACTION_DOWN) {
625 //computeNextTarget();
626 //nextx=Math.round(event.getX());
627 //nexty=Math.round(event.getY());
629 return super.onTouchEvent(event);
632 private void initialize() {
634 height = getHeight();
636 bg = BitmapFactory.decodeResource(getResources(), R.drawable.kshisen_bgnd);
637 Bitmap tileset = BitmapFactory.decodeResource(getResources(), R.drawable.tileset);
639 // The tile set has 4 rows x 9 columns
642 twidth = tileset.getWidth()/tscols;
643 theight = tileset.getHeight()/tsrows;
644 tile = new Bitmap[tsrows*tscols];
646 for (int i=0; i<tsrows; i++) {
647 for (int j=0; j<tscols; j++) {
648 tile[k] = Bitmap.createBitmap(tileset, j*twidth, i*theight, twidth, theight, null, false);
661 private void computeNextTarget() {
662 startTime = System.currentTimeMillis();
665 nextx = (int) Math.floor(Math.random() * width);
666 nexty = (int) Math.floor(Math.random() * height);
667 randomtile = (int) Math.floor(Math.random() * tile.length);
670 paint.setColor(Color.parseColor("#006666"));
671 paint.setFlags(Paint.ANTI_ALIAS_FLAG);