1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.tictactoe.library;
18 
19 import java.util.Random;
20 
21 import android.app.Activity;
22 import android.os.Bundle;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.os.Handler.Callback;
26 import android.view.View;
27 import android.view.View.OnClickListener;
28 import android.widget.Button;
29 import android.widget.TextView;
30 
31 import com.example.android.tictactoe.library.GameView.ICellListener;
32 import com.example.android.tictactoe.library.GameView.State;
33 
34 
35 public class GameActivity extends Activity {
36 
37     /** Start player. Must be 1 or 2. Default is 1. */
38     public static final String EXTRA_START_PLAYER =
39         "com.example.android.tictactoe.library.GameActivity.EXTRA_START_PLAYER";
40 
41     private static final int MSG_COMPUTER_TURN = 1;
42     private static final long COMPUTER_DELAY_MS = 500;
43 
44     private Handler mHandler = new Handler(new MyHandlerCallback());
45     private Random mRnd = new Random();
46     private GameView mGameView;
47     private TextView mInfoView;
48     private Button mButtonNext;
49 
50     /** Called when the activity is first created. */
51     @Override
onCreate(Bundle bundle)52     public void onCreate(Bundle bundle) {
53         super.onCreate(bundle);
54 
55         /*
56          * IMPORTANT: all resource IDs from this library will eventually be merged
57          * with the resources from the main project that will use the library.
58          *
59          * If the main project and the libraries define the same resource IDs,
60          * the application project will always have priority and override library resources
61          * and IDs defined in multiple libraries are resolved based on the libraries priority
62          * defined in the main project.
63          *
64          * An intentional consequence is that the main project can override some resources
65          * from the library.
66          * (TODO insert example).
67          *
68          * To avoid potential conflicts, it is suggested to add a prefix to the
69          * library resource names.
70          */
71         setContentView(R.layout.lib_game);
72 
73         mGameView = (GameView) findViewById(R.id.game_view);
74         mInfoView = (TextView) findViewById(R.id.info_turn);
75         mButtonNext = (Button) findViewById(R.id.next_turn);
76 
77         mGameView.setFocusable(true);
78         mGameView.setFocusableInTouchMode(true);
79         mGameView.setCellListener(new MyCellListener());
80 
81         mButtonNext.setOnClickListener(new MyButtonListener());
82     }
83 
84     @Override
onResume()85     protected void onResume() {
86         super.onResume();
87 
88         State player = mGameView.getCurrentPlayer();
89         if (player == State.UNKNOWN) {
90             player = State.fromInt(getIntent().getIntExtra(EXTRA_START_PLAYER, 1));
91             if (!checkGameFinished(player)) {
92                 selectTurn(player);
93             }
94         }
95         if (player == State.PLAYER2) {
96             mHandler.sendEmptyMessageDelayed(MSG_COMPUTER_TURN, COMPUTER_DELAY_MS);
97         }
98         if (player == State.WIN) {
99             setWinState(mGameView.getWinner());
100         }
101     }
102 
103 
selectTurn(State player)104     private State selectTurn(State player) {
105         mGameView.setCurrentPlayer(player);
106         mButtonNext.setEnabled(false);
107 
108         if (player == State.PLAYER1) {
109             mInfoView.setText(R.string.player1_turn);
110             mGameView.setEnabled(true);
111 
112         } else if (player == State.PLAYER2) {
113             mInfoView.setText(R.string.player2_turn);
114             mGameView.setEnabled(false);
115         }
116 
117         return player;
118     }
119 
120     private class MyCellListener implements ICellListener {
onCellSelected()121         public void onCellSelected() {
122             if (mGameView.getCurrentPlayer() == State.PLAYER1) {
123                 int cell = mGameView.getSelection();
124                 mButtonNext.setEnabled(cell >= 0);
125             }
126         }
127     }
128 
129     private class MyButtonListener implements OnClickListener {
130 
onClick(View v)131         public void onClick(View v) {
132             State player = mGameView.getCurrentPlayer();
133 
134             if (player == State.WIN) {
135                 GameActivity.this.finish();
136 
137             } else if (player == State.PLAYER1) {
138                 int cell = mGameView.getSelection();
139                 if (cell >= 0) {
140                     mGameView.stopBlink();
141                     mGameView.setCell(cell, player);
142                     finishTurn();
143                 }
144             }
145         }
146     }
147 
148     private class MyHandlerCallback implements Callback {
handleMessage(Message msg)149         public boolean handleMessage(Message msg) {
150             if (msg.what == MSG_COMPUTER_TURN) {
151 
152                 // Pick a non-used cell at random. That's about all the AI you need for this game.
153                 State[] data = mGameView.getData();
154                 int used = 0;
155                 while (used != 0x1F) {
156                     int index = mRnd.nextInt(9);
157                     if (((used >> index) & 1) == 0) {
158                         used |= 1 << index;
159                         if (data[index] == State.EMPTY) {
160                             mGameView.setCell(index, mGameView.getCurrentPlayer());
161                             break;
162                         }
163                     }
164                 }
165 
166                 finishTurn();
167                 return true;
168             }
169             return false;
170         }
171     }
172 
getOtherPlayer(State player)173     private State getOtherPlayer(State player) {
174         return player == State.PLAYER1 ? State.PLAYER2 : State.PLAYER1;
175     }
176 
finishTurn()177     private void finishTurn() {
178         State player = mGameView.getCurrentPlayer();
179         if (!checkGameFinished(player)) {
180             player = selectTurn(getOtherPlayer(player));
181             if (player == State.PLAYER2) {
182                 mHandler.sendEmptyMessageDelayed(MSG_COMPUTER_TURN, COMPUTER_DELAY_MS);
183             }
184         }
185     }
186 
checkGameFinished(State player)187     public boolean checkGameFinished(State player) {
188         State[] data = mGameView.getData();
189         boolean full = true;
190 
191         int col = -1;
192         int row = -1;
193         int diag = -1;
194 
195         // check rows
196         for (int j = 0, k = 0; j < 3; j++, k += 3) {
197             if (data[k] != State.EMPTY && data[k] == data[k+1] && data[k] == data[k+2]) {
198                 row = j;
199             }
200             if (full && (data[k] == State.EMPTY ||
201                          data[k+1] == State.EMPTY ||
202                          data[k+2] == State.EMPTY)) {
203                 full = false;
204             }
205         }
206 
207         // check columns
208         for (int i = 0; i < 3; i++) {
209             if (data[i] != State.EMPTY && data[i] == data[i+3] && data[i] == data[i+6]) {
210                 col = i;
211             }
212         }
213 
214         // check diagonals
215         if (data[0] != State.EMPTY && data[0] == data[1+3] && data[0] == data[2+6]) {
216             diag = 0;
217         } else  if (data[2] != State.EMPTY && data[2] == data[1+3] && data[2] == data[0+6]) {
218             diag = 1;
219         }
220 
221         if (col != -1 || row != -1 || diag != -1) {
222             setFinished(player, col, row, diag);
223             return true;
224         }
225 
226         // if we get here, there's no winner but the board is full.
227         if (full) {
228             setFinished(State.EMPTY, -1, -1, -1);
229             return true;
230         }
231         return false;
232     }
233 
setFinished(State player, int col, int row, int diagonal)234     private void setFinished(State player, int col, int row, int diagonal) {
235 
236         mGameView.setCurrentPlayer(State.WIN);
237         mGameView.setWinner(player);
238         mGameView.setEnabled(false);
239         mGameView.setFinished(col, row, diagonal);
240 
241         setWinState(player);
242     }
243 
setWinState(State player)244     private void setWinState(State player) {
245         mButtonNext.setEnabled(true);
246         mButtonNext.setText("Back");
247 
248         String text;
249 
250         if (player == State.EMPTY) {
251             text = getString(R.string.tie);
252         } else if (player == State.PLAYER1) {
253             text = getString(R.string.player1_win);
254         } else {
255             text = getString(R.string.player2_win);
256         }
257         mInfoView.setText(text);
258     }
259 }
260