update,
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
package com.game.tictacteo;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.game.tictacteo.engine.Game;
|
||||
import com.game.tictacteo.engine.GameBoard;
|
||||
import com.game.tictacteo.engine.GameBoardPlacedException;
|
||||
import com.game.tictacteo.engine.GameStatus;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class GameActivity extends AppCompatActivity implements View.OnClickListener {
|
||||
|
||||
private HashMap<Integer, LocateButton> gameBoardMap;
|
||||
private Game game;
|
||||
private ImageView imGameMes;
|
||||
private final static byte USER_PLAYER = 1;
|
||||
private final static byte AI_PLAYER = 2;
|
||||
//private ArrayList<Integer> lockedButton;
|
||||
private ImageView imDisPlayer;
|
||||
private ImageView imDisAI;
|
||||
private Button btnContinue;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_game);
|
||||
//this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
|
||||
imGameMes = (ImageView) findViewById(R.id.txtGameMes);
|
||||
imDisPlayer = (ImageView) findViewById(R.id.imDisplayPlayer);
|
||||
imDisAI = (ImageView) findViewById(R.id.imDisplayAI);
|
||||
btnContinue = (Button) findViewById(R.id.btnContinue);
|
||||
btnContinue.setVisibility(View.GONE);
|
||||
|
||||
// Close button
|
||||
btnContinue.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
finish();
|
||||
startActivity(getIntent());
|
||||
}
|
||||
});
|
||||
|
||||
// Button Map to reduce the runtime
|
||||
gameBoardMap = new HashMap<Integer, LocateButton>();
|
||||
|
||||
for(int i = 0; i < 3; i++){
|
||||
for (int j = 0; j < 3; j++){
|
||||
String buttonID = "btn_"+i+"_"+j;
|
||||
int resourceID = getResources().getIdentifier(buttonID, "id", getPackageName());
|
||||
Button button = (Button) findViewById(resourceID);
|
||||
button.setOnClickListener(this);
|
||||
Log.i("gameAc-onCreate", "refesh");
|
||||
gameBoardMap.put(resourceID, new LocateButton(button, new int[]{i, j}));
|
||||
}
|
||||
}
|
||||
Log.i("gameAc", "start");
|
||||
|
||||
game = new Game(GameActivity.this);
|
||||
//lockedButton = new ArrayList<Integer>();
|
||||
}
|
||||
|
||||
// refesh the game board
|
||||
public void refesh(byte[][] board){
|
||||
Log.i("gameAc", "refesh");
|
||||
for(int i = 0; i < board.length; i++){
|
||||
for(int j = 0; j < board[i].length; j++){
|
||||
|
||||
String mapKey = "btn_"+i+"_"+j;
|
||||
int resourceID = getResources().getIdentifier(mapKey, "id", getPackageName());
|
||||
|
||||
// Get button from hashmap
|
||||
Button button = gameBoardMap.get(resourceID).button;
|
||||
|
||||
// set button image and disable clicked button
|
||||
if(board[i][j] == USER_PLAYER){
|
||||
button.setBackgroundResource(R.drawable.ic_o);
|
||||
button.setClickable(false);
|
||||
}else if (board[i][j] == AI_PLAYER){
|
||||
button.setBackgroundResource(R.drawable.ic_x);
|
||||
button.setClickable(false);
|
||||
}else{
|
||||
button.setBackgroundResource(R.drawable.game_board_pos);
|
||||
button.setClickable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disable all button
|
||||
public void lockAllButton(){
|
||||
for(LocateButton lb: gameBoardMap.values())
|
||||
lb.button.setClickable(false);
|
||||
}
|
||||
|
||||
// public void unlockAllButton(){
|
||||
// for(Integer key: gameBoardMap.keySet()){
|
||||
// Button button = gameBoardMap.get(key).button;
|
||||
// if(lockedButton.contains(key))
|
||||
// button.setClickable(false);
|
||||
// else
|
||||
// button.setClickable(true);
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
Log.i("gameAc", view.getId() +"");
|
||||
if (gameBoardMap.containsKey(view.getId())) {
|
||||
LocateButton lb = gameBoardMap.get(view.getId());
|
||||
|
||||
// Player place
|
||||
lockAllButton();
|
||||
|
||||
// set clicked button UI setting
|
||||
lb.button.setBackgroundResource(R.drawable.ic_o);
|
||||
imGameMes.setImageResource(R.drawable.game_mes_2);
|
||||
imDisPlayer.setBackgroundResource(0);
|
||||
imDisAI.setBackgroundResource(R.drawable.btnranking);
|
||||
|
||||
//lockedButton.add(view.getId());
|
||||
|
||||
try {
|
||||
|
||||
// Place the index
|
||||
GameStatus gs = game.player1Place(lb.locate[0], lb.locate[1]);
|
||||
this.refesh(gs.board);
|
||||
|
||||
imGameMes.setImageResource(R.drawable.game_mes_1);
|
||||
imDisAI.setBackgroundResource(0);
|
||||
|
||||
// Check returned game statue and update the UI
|
||||
if(gs.status == GameBoard.Status.CONTINUE) {
|
||||
imDisPlayer.setBackgroundResource(R.drawable.btnranking);
|
||||
//unlockAllButton();
|
||||
}else{
|
||||
|
||||
if (gs.status == GameBoard.Status.DRAW){
|
||||
imDisPlayer.setBackgroundResource(R.drawable.btnranking);
|
||||
imGameMes.setImageResource(R.drawable.game_dis_draw);
|
||||
}else if (gs.status == GameBoard.Status.PLAYER1_WIN){
|
||||
imGameMes.setImageResource(R.drawable.game_dis_win);
|
||||
}else if(gs.status == GameBoard.Status.PLAYER2_WIN) {
|
||||
imDisPlayer.setBackgroundResource(0);
|
||||
imDisAI.setBackgroundResource(R.drawable.btnranking);
|
||||
imGameMes.setImageResource(R.drawable.game_dis_lose);
|
||||
}
|
||||
btnContinue.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
} catch (GameBoardPlacedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class LocateButton {
|
||||
Button button;
|
||||
int[] locate; // row, col index
|
||||
|
||||
LocateButton(Button button, int[]locate){
|
||||
this.button = button;
|
||||
this.locate = locate;
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package com.game.tictacteo;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class InitActivity extends AppCompatActivity {
|
||||
|
||||
private TextView tvLoadingMes;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_init);
|
||||
// hidden Action bar
|
||||
// getSupportActionBar().hide();
|
||||
|
||||
tvLoadingMes = (TextView) findViewById(R.id.tvLoadingMes);
|
||||
// For Some setup things
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Intent intent = new Intent(InitActivity.this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.game.tictacteo;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class LoadingDialog {
|
||||
|
||||
Context context;
|
||||
Dialog dialog;
|
||||
TextView tvLoadingDailogMes;
|
||||
|
||||
public LoadingDialog(Context context){
|
||||
this.context = context;
|
||||
dialog = new Dialog(context);
|
||||
dialog.setContentView(R.layout.activity_loading_dialog);
|
||||
tvLoadingDailogMes = (TextView) dialog.findViewById(R.id.tvLoadingDialogMes);
|
||||
}
|
||||
|
||||
public void show(String message){
|
||||
|
||||
|
||||
dialog.getWindow().setBackgroundDrawable(new ColorDrawable((Color.TRANSPARENT)));
|
||||
// Disable click outside then close the loading dialog
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
// set Header
|
||||
tvLoadingDailogMes.setText(message);
|
||||
dialog.create();
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void hide(){
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
package com.game.tictacteo;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
Button btnStartGame;
|
||||
Button btnRanking;
|
||||
Button btnRecords;
|
||||
Button btnClose;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
btnStartGame = (Button) findViewById(R.id.btnPlay);
|
||||
btnRanking = (Button) findViewById(R.id.btnRanking);
|
||||
btnRecords = (Button) findViewById(R.id.btnRec);
|
||||
btnClose = (Button) findViewById(R.id.btnClose);
|
||||
|
||||
// Start game
|
||||
btnStartGame.setOnClickListener( new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent( MainActivity.this, GameActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Game Ranking
|
||||
btnRanking.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent( MainActivity.this, RankingActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// Your Records
|
||||
btnRecords.setOnClickListener( new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent( MainActivity.this, RecordActivity.class);
|
||||
startActivity(intent);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Close
|
||||
btnClose.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
System.out.println("click");
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
package com.game.tictacteo;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.game.tictacteo.model.UserRanking;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class RankingActivity extends AppCompatActivity {
|
||||
|
||||
private Button btnBack;
|
||||
private final static String API_ENDPOINT = "http://192.168.76.3/ranking_api.php";
|
||||
private LoadingDialog ld;
|
||||
private ArrayList<UserRanking> users;
|
||||
private RecyclerView rvRankingListView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_ranking);
|
||||
// Load Dailog
|
||||
ld = new LoadingDialog(this);
|
||||
users = new ArrayList<UserRanking>();
|
||||
|
||||
btnBack = (Button) findViewById(R.id.btnBack);
|
||||
rvRankingListView = (RecyclerView) findViewById(R.id.rvRankingList);
|
||||
btnBack.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
// show loading dialog
|
||||
ld.show("fetching...");
|
||||
new Thread(() -> {
|
||||
try{
|
||||
// fetch API
|
||||
URL url = new URL(API_ENDPOINT);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
BufferedReader buffer = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String data = buffer.readLine();
|
||||
StringBuffer json = new StringBuffer();
|
||||
while(data != null){
|
||||
json.append(data);
|
||||
data = buffer.readLine();
|
||||
}
|
||||
// JSON List to ArrayList
|
||||
JSONArray jsonArray = new JSONArray(String.valueOf(json));
|
||||
for(int i = 0; i < jsonArray.length(); i++){
|
||||
JSONObject jsonObj = jsonArray.getJSONObject(i);
|
||||
String name = jsonObj.getString("Name");
|
||||
int duration = jsonObj.getInt("Duration");
|
||||
users.add(new UserRanking(name, duration));
|
||||
Log.i("ranking", "add" + name);
|
||||
}
|
||||
|
||||
runOnUiThread(() -> {
|
||||
ld.hide();
|
||||
UserRankingAdapter rankingAdapter = new UserRankingAdapter(this, users);
|
||||
rvRankingListView.setAdapter(rankingAdapter);
|
||||
rvRankingListView.setLayoutManager(new LinearLayoutManager(RankingActivity.this));
|
||||
});
|
||||
|
||||
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
).start();
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
package com.game.tictacteo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.game.tictacteo.localDB.GameLogDB;
|
||||
import com.game.tictacteo.model.GameLog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class RecordActivity extends AppCompatActivity {
|
||||
|
||||
|
||||
private Button btnRecordBack;
|
||||
private SurfaceView winStatePieChartView;
|
||||
private SurfaceHolder holder;
|
||||
private RecyclerView rvGameLogList;
|
||||
private ArrayList<GameLog> logs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_record);
|
||||
logs = GameLogDB.getInstance(this).readAllData();
|
||||
winStatePieChartView = (SurfaceView) findViewById(R.id.sfwinStatePieChartView);
|
||||
|
||||
// For get Canvas in SurfaceView
|
||||
holder = winStatePieChartView.getHolder();
|
||||
|
||||
if(logs.size() > 0){
|
||||
|
||||
// If not callback will throw error due to SurfaceView.canvas is not created in onCreate()
|
||||
holder.addCallback(new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
|
||||
|
||||
float[] perc = calPercentage(logs);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
paint.setStrokeWidth(4);
|
||||
|
||||
Canvas canvas = holder.lockCanvas();
|
||||
|
||||
RectF rectF = new RectF(225,50,winStatePieChartView.getWidth()-225,winStatePieChartView.getHeight()-50);
|
||||
// draw the lose pie
|
||||
paint.setColor(Color.RED);
|
||||
canvas.drawArc(rectF, 0, 360*perc[0], true, paint);
|
||||
|
||||
// draw the win pie
|
||||
paint.setColor(Color.GREEN);
|
||||
canvas.drawArc(rectF, 360*perc[0], 360*perc[1], true, paint);
|
||||
|
||||
// draw the draw pie
|
||||
paint.setColor(Color.YELLOW);
|
||||
canvas.drawArc(rectF, 360*perc[0]+360*perc[1], 360*perc[2], true, paint);
|
||||
|
||||
// setup text paint
|
||||
paint.setStrokeWidth(0);
|
||||
paint.setTextSize(30);
|
||||
|
||||
// write Draw Text
|
||||
canvas.drawRect(winStatePieChartView.getWidth()-160, winStatePieChartView.getHeight()-200, winStatePieChartView.getWidth()-135, winStatePieChartView.getHeight()-175, paint);
|
||||
canvas.drawText("DRAW", winStatePieChartView.getWidth()-125, winStatePieChartView.getHeight()-177, paint);
|
||||
|
||||
// write Lose Text
|
||||
paint.setColor(Color.RED);
|
||||
canvas.drawRect(winStatePieChartView.getWidth()-160, winStatePieChartView.getHeight()-150, winStatePieChartView.getWidth()-135, winStatePieChartView.getHeight()-125, paint);
|
||||
canvas.drawText("LOSE", winStatePieChartView.getWidth()-125, winStatePieChartView.getHeight()-127, paint);
|
||||
|
||||
// write Win Text
|
||||
paint.setColor(Color.GREEN);
|
||||
canvas.drawRect(winStatePieChartView.getWidth()-160, winStatePieChartView.getHeight()-100, winStatePieChartView.getWidth()-135, winStatePieChartView.getHeight()-75, paint);
|
||||
canvas.drawText("WIN", winStatePieChartView.getWidth()-125, winStatePieChartView.getHeight()-77, paint);
|
||||
|
||||
holder.unlockCanvasAndPost(canvas);
|
||||
|
||||
//holder.lockCanvas(new Rect(0, 0, 0, 0));
|
||||
//holder.unlockCanvasAndPost(canvas);
|
||||
}
|
||||
|
||||
private float[] calPercentage(ArrayList<GameLog> data){
|
||||
int[] count = new int[3]; // 0 -> lose, 1 -> win, 2 -> draw
|
||||
int total = 0;
|
||||
for(GameLog gl: data ) {
|
||||
count[gl.getWinningStatus()]++;
|
||||
total++;
|
||||
}
|
||||
Log.i("prc1", count[0]*1.0f/total+"");
|
||||
Log.i("prc2", count[1]*1.0f/total+"");
|
||||
Log.i("prc3", count[2]*1.0f/total+"");
|
||||
return new float[]{count[0]*1.0f/total, count[1]*1.0f/total, count[2]*1.0f/total};
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
rvGameLogList = (RecyclerView) findViewById(R.id.rvGameLogList);
|
||||
|
||||
btnRecordBack = (Button) findViewById(R.id.btnRecordBack);
|
||||
btnRecordBack.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
// create adapter for attached the log list
|
||||
UserRecordAdapter rankingAdapter = new UserRecordAdapter(this, logs);
|
||||
rvGameLogList.setAdapter(rankingAdapter);
|
||||
rvGameLogList.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
package com.game.tictacteo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.game.tictacteo.model.UserRanking;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class UserRankingAdapter extends RecyclerView.Adapter<UserRankingAdapter.Viewholder> {
|
||||
|
||||
private Context context;
|
||||
private ArrayList<UserRanking> users;
|
||||
|
||||
// Constructor
|
||||
public UserRankingAdapter(Context context, ArrayList<UserRanking> users) {
|
||||
this.context = context;
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UserRankingAdapter.Viewholder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_ranking_card_row, parent, false);
|
||||
return new Viewholder(view);
|
||||
}
|
||||
|
||||
|
||||
// Set Text for each row
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull UserRankingAdapter.Viewholder holder, int position) {
|
||||
UserRanking user = users.get(position);
|
||||
holder.tvRankingNo.setText(String.valueOf(position+1));
|
||||
holder.tvRankingName.setText("Name: " + user.getName());
|
||||
holder.tvRankingDuration.setText("Duration: " + String.valueOf(user.getDuration()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return users.size();
|
||||
}
|
||||
|
||||
// Init find Element ID;
|
||||
public class Viewholder extends RecyclerView.ViewHolder {
|
||||
private TextView tvRankingNo, tvRankingName, tvRankingDuration;
|
||||
|
||||
public Viewholder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvRankingNo = itemView.findViewById(R.id.tvRecordDateTime);
|
||||
tvRankingName = itemView.findViewById(R.id.tvRecordState);
|
||||
tvRankingDuration = itemView.findViewById(R.id.tvRecordDuration);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
package com.game.tictacteo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.game.tictacteo.model.GameLog;
|
||||
import com.game.tictacteo.model.UserRanking;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class UserRecordAdapter extends RecyclerView.Adapter<UserRecordAdapter.Viewholder> {
|
||||
|
||||
private Context context;
|
||||
private ArrayList<GameLog> logs;
|
||||
|
||||
public UserRecordAdapter(Context context, ArrayList<GameLog> logs) {
|
||||
this.context = context;
|
||||
this.logs = logs;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public UserRecordAdapter.Viewholder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_record_card_row, parent, false);
|
||||
return new Viewholder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull UserRecordAdapter.Viewholder holder, int position) {
|
||||
GameLog log = logs.get(position);
|
||||
|
||||
SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd");
|
||||
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm aa");
|
||||
|
||||
holder.tvRecordDateTime.setText(dateFormat.format(log.getPlayDate()) + " " + timeFormat.format(log.getPlayTime()));
|
||||
holder.tvRecordState.setText("WinState: " + log.winStateToString());
|
||||
if(log.getWinningStatus() == 0)
|
||||
holder.tvRecordState.setTextColor(Color.RED);
|
||||
else if (log.getWinningStatus() == 1)
|
||||
holder.tvRecordState.setTextColor(Color.GREEN);
|
||||
else
|
||||
holder.tvRecordState.setTextColor(Color.BLACK);
|
||||
|
||||
holder.tvRecordDuration.setText("Duration: " + String.valueOf(log.getDuration()) + " sec");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return logs.size();
|
||||
}
|
||||
|
||||
public class Viewholder extends RecyclerView.ViewHolder {
|
||||
private TextView tvRecordDateTime, tvRecordState, tvRecordDuration;
|
||||
|
||||
public Viewholder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvRecordDateTime = itemView.findViewById(R.id.tvRecordDateTime);
|
||||
tvRecordState = itemView.findViewById(R.id.tvRecordState);
|
||||
tvRecordDuration = itemView.findViewById(R.id.tvRecordDuration);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
package com.game.tictacteo.engine;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.game.tictacteo.localDB.GameLogDB;
|
||||
import com.game.tictacteo.model.GameLog;
|
||||
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
|
||||
public class Game {
|
||||
|
||||
private Timestamp startTime;
|
||||
private Timestamp endTime;
|
||||
private GameBoard board;
|
||||
private Context ctx;
|
||||
|
||||
|
||||
public Game(Context ctx) {
|
||||
startTime = new Timestamp(System.currentTimeMillis());
|
||||
board = new GameBoard();
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
// User move
|
||||
public GameStatus player1Place(int row, int col) throws GameBoardPlacedException {
|
||||
|
||||
GameStatus gs = place(row, col, 1);
|
||||
checkGameStatus(gs); // check status if won not need AI move
|
||||
if (gs.status == GameBoard.Status.CONTINUE) {
|
||||
gs = aiPlayerPlace();
|
||||
checkGameStatus(gs); // check status
|
||||
}
|
||||
return gs;
|
||||
}
|
||||
|
||||
private GameStatus place(int row, int col, int player) throws GameBoardPlacedException {
|
||||
|
||||
if (player == 1)
|
||||
return this.board.placePlayer1(row, col);
|
||||
else if (player == 2)
|
||||
return this.board.placePlayer2(row, col);
|
||||
return null;
|
||||
}
|
||||
|
||||
private void checkGameStatus(GameStatus gs) {
|
||||
// check status if Player1 or Player2 win need to insert the log to db
|
||||
if (gs.status != GameBoard.Status.CONTINUE) {
|
||||
endTime = new Timestamp(System.currentTimeMillis());
|
||||
int duration = (int) ((endTime.getTime() - startTime.getTime()) / 1000);
|
||||
|
||||
int winingStatus = ((gs.status == GameBoard.Status.DRAW) ? 2 : (gs.status == GameBoard.Status.PLAYER2_WIN) ? 0 : 1);
|
||||
GameLogDB.getInstance(this.ctx).addLog(new GameLog(new Date(endTime.getTime()), new Time(endTime.getTime()), duration, winingStatus));
|
||||
}
|
||||
}
|
||||
|
||||
// AI move
|
||||
private GameStatus aiPlayerPlace() throws GameBoardPlacedException {
|
||||
Log.i("game-OBJ-AI", "aiPLayerPlace");
|
||||
int[] move = GameAI.findBestMove(board.getBoard());
|
||||
return place(move[0], move[1], 2);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
package com.game.tictacteo.engine;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class GameAI {
|
||||
|
||||
static final byte player = 2;
|
||||
static final byte opponent = 1;
|
||||
|
||||
private static Boolean isMovesLeft(byte board[][]) {
|
||||
for (int i = 0; i < board.length; i++)
|
||||
for (int j = 0; j < board[i].length; j++)
|
||||
if (board[i][j] == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int evaluate(byte[][] board) {
|
||||
// Checking for Rows is player or opponent victory.
|
||||
for (int row = 0; row < board.length; row++) {
|
||||
if (board[row][0] == board[row][1] &&
|
||||
board[row][1] == board[row][2]) {
|
||||
if (board[row][0] == player)
|
||||
return +10;
|
||||
else if (board[row][0] == opponent)
|
||||
return -10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int col = 0; col < board[0].length; col++) {
|
||||
if (board[0][col] == board[1][col] &&
|
||||
board[1][col] == board[2][col]) {
|
||||
if (board[0][col] == player)
|
||||
return +10;
|
||||
|
||||
else if (board[0][col] == opponent)
|
||||
return -10;
|
||||
}
|
||||
}
|
||||
|
||||
if (board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
|
||||
if (board[0][0] == player)
|
||||
return +10;
|
||||
else if (board[0][0] == opponent)
|
||||
return -10;
|
||||
}
|
||||
|
||||
if (board[0][2] == board[1][1] && board[1][1] == board[2][0]) {
|
||||
if (board[0][2] == player)
|
||||
return +10;
|
||||
else if (board[0][2] == opponent)
|
||||
return -10;
|
||||
}
|
||||
|
||||
// Else if none of them have won then return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int minimax(byte board[][], int depth, Boolean isMax) {
|
||||
int score = evaluate(board);
|
||||
|
||||
// Evaluated score if Maximizer has won the game
|
||||
if (score == 10)
|
||||
return score;
|
||||
|
||||
if (score == -10)
|
||||
return score;
|
||||
|
||||
// no winner then it is a tie if false
|
||||
if (isMovesLeft(board) == false)
|
||||
return 0;
|
||||
|
||||
if (isMax) {
|
||||
int best = -1000;
|
||||
|
||||
// Traverse all cells
|
||||
for (int i = 0; i < board.length; i++) {
|
||||
for (int j = 0; j < board[i].length; j++) {
|
||||
// Check cell is empty
|
||||
if (board[i][j] == 0) {
|
||||
// Make the move
|
||||
board[i][j] = player;
|
||||
|
||||
// calculate minimax recursively and choose
|
||||
best = Math.max(best, minimax(board, depth + 1, !isMax));
|
||||
|
||||
// Undo move
|
||||
board[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}else {
|
||||
int best = 1000;
|
||||
|
||||
// Traverse all cells
|
||||
for (int i = 0; i < board.length; i++) {
|
||||
for (int j = 0; j < board[i].length; j++) {
|
||||
// Check cell is empty
|
||||
if (board[i][j] == 0) {
|
||||
// Make the move
|
||||
board[i][j] = opponent;
|
||||
|
||||
// calculate minimax recursively and choose
|
||||
best = Math.min(best, minimax(board,
|
||||
depth + 1, !isMax));
|
||||
|
||||
// Undo move
|
||||
board[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] findBestMove(byte board[][]) {
|
||||
int bestVal = -1000;
|
||||
int[] bestMove = new int[2];
|
||||
bestMove[0] = -1;
|
||||
bestMove[1] = -1;
|
||||
|
||||
// evaluate minimax func
|
||||
for (int i = 0; i < board.length; i++) {
|
||||
for (int j = 0; j < board[i].length; j++) {
|
||||
// Check cell is empty
|
||||
if (board[i][j] == 0) {
|
||||
// Make the move
|
||||
board[i][j] = player;
|
||||
|
||||
// compute evaluation function
|
||||
int moveVal = minimax(board, 0, false);
|
||||
|
||||
// Undo the move
|
||||
board[i][j] = 0;
|
||||
|
||||
// If the value of the current move is higher then best value, then update
|
||||
if (moveVal > bestVal) {
|
||||
bestMove[0] = i;
|
||||
bestMove[1] = j;
|
||||
bestVal = moveVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMove;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
package com.game.tictacteo.engine;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class GameBoard {
|
||||
|
||||
private final static int ROW = 3; // Game Board ROW
|
||||
private final static int COL = 3; // Game Board COL
|
||||
|
||||
private byte[][] board;
|
||||
private short step;
|
||||
private short winner; // Winner
|
||||
|
||||
public enum Status {
|
||||
CONTINUE,
|
||||
PLAYER1_WIN,
|
||||
PLAYER2_WIN,
|
||||
DRAW
|
||||
}
|
||||
|
||||
// 0 0 0
|
||||
// 0 0 0
|
||||
// 0 0 0
|
||||
|
||||
GameBoard() {
|
||||
board = new byte[ROW][COL];
|
||||
step = 0;
|
||||
winner = -1;
|
||||
}
|
||||
|
||||
public byte[][] getBoard() {
|
||||
return board;
|
||||
}
|
||||
|
||||
public GameStatus placePlayer1(int row, int col) throws GameBoardPlacedException {
|
||||
return place(row, col, 1);
|
||||
}
|
||||
|
||||
public GameStatus placePlayer2(int row, int col) throws GameBoardPlacedException{
|
||||
return place(row, col, 2);
|
||||
}
|
||||
|
||||
|
||||
private GameStatus place(int row, int col, int player) throws GameBoardPlacedException {
|
||||
if(board[row][col] != 0)
|
||||
throw new GameBoardPlacedException("The position is placed: " + row + " " + col);
|
||||
board[row][col] = (byte) player;
|
||||
step++; // count step for game
|
||||
return genGameStatus();
|
||||
}
|
||||
|
||||
private GameStatus genGameStatus(){
|
||||
|
||||
// return current game board status
|
||||
// step == board.length mean all is placed
|
||||
if(step == board[0].length * board.length)
|
||||
return new GameStatus(Status.DRAW, this.board);
|
||||
|
||||
// check has winner?
|
||||
if(winner == -1 && !checkWin())
|
||||
return new GameStatus(Status.CONTINUE, this.board);
|
||||
else
|
||||
return new GameStatus((this.winner == 1) ? Status.PLAYER1_WIN: Status.PLAYER2_WIN, this.board);
|
||||
|
||||
}
|
||||
|
||||
private boolean checkWin(){
|
||||
return (checkRows() || checkColumns() || checkDiagonals());
|
||||
}
|
||||
|
||||
|
||||
private boolean checkRows() {
|
||||
for (int i = 0; i < board.length; i++) {
|
||||
int count = 1;
|
||||
for (int j = 1; j < board[i].length; j++) {
|
||||
// compare other col is same?
|
||||
if (board[i][0] != 0 && board[i][0] == board[i][j])
|
||||
count++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (count == board[i].length){
|
||||
this.winner = board[i][0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkColumns() {
|
||||
for (int i = 0; i < board[0].length; i++) {
|
||||
int count = 1;
|
||||
for (int j = 1; j < board.length; j++) {
|
||||
// compare other col is same?
|
||||
if (board[0][i] != 0 && board[0][i] == board[j][i])
|
||||
count++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (count == board.length) {
|
||||
this.winner = board[0][i];
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// checkDiagonals
|
||||
private boolean checkDiagonals() {
|
||||
int countX = 1; // for count left-top to right-bottom
|
||||
int countY = 1; // for count right-top to left-bottom
|
||||
for (int j = 1; j < board.length; j++) {
|
||||
// compare other col is same?
|
||||
if (board[0][0] != 0 && board[0][0] == board[j][j])
|
||||
countX++;
|
||||
if (board[0][board[0].length - 1] != 0 && board[0][board[0].length - 1] == board[j][board[0].length - 1 - j])
|
||||
countY++;
|
||||
}
|
||||
|
||||
|
||||
if (countX == board.length) {
|
||||
this.winner = board[0][0];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (countY == board.length) {
|
||||
this.winner = board[0][board[0].length-1];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
package com.game.tictacteo.engine;
|
||||
|
||||
public class GameBoardPlacedException extends Exception{
|
||||
public GameBoardPlacedException(String mes){
|
||||
super(mes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,12 @@
|
||||
package com.game.tictacteo.engine;
|
||||
|
||||
public class GameStatus {
|
||||
|
||||
public GameBoard.Status status;
|
||||
public byte[][] board;
|
||||
|
||||
public GameStatus(GameBoard.Status status, byte[][] board){
|
||||
this.status = status;
|
||||
this.board = board;
|
||||
}
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
package com.game.tictacteo.localDB;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.game.tictacteo.model.GameLog;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.Time;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GameLogDB extends SQLiteOpenHelper {
|
||||
|
||||
private static final String DATABASE_NAME = "GameLog.db";
|
||||
private static final String GAMESLOG_TABLE_NAME = "GamesLog";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
// Singleton pattern
|
||||
private static GameLogDB instance = null;
|
||||
|
||||
public static GameLogDB getInstance(Context ctx) {
|
||||
if (instance == null)
|
||||
instance = new GameLogDB(ctx.getApplicationContext());
|
||||
return instance;
|
||||
}
|
||||
|
||||
public GameLogDB(Context context) {
|
||||
// applcation_context, db_name, factory, db_version
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
System.out.println("Database creating");
|
||||
String sql = "CREATE TABLE " + GAMESLOG_TABLE_NAME + " ( " +
|
||||
"gameID INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||
"playDate TEXT NOT NULL, " +
|
||||
"playTime TEXT NOT NULL, " +
|
||||
"duration INTEGER NOT NULL, " +
|
||||
"winningStatus INTEGER NOT NULL);";
|
||||
|
||||
db.execSQL(sql);
|
||||
|
||||
}
|
||||
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + GAMESLOG_TABLE_NAME + ";");
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
// Add log
|
||||
public void addLog(GameLog log) {
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm aa");
|
||||
|
||||
values.put("playDate", dateFormat.format(log.getPlayDate()));
|
||||
values.put("playTime", timeFormat.format(log.getPlayTime()));
|
||||
values.put("duration", log.getDuration());
|
||||
values.put("winningStatus", log.getWinningStatus());
|
||||
|
||||
db.insert(GAMESLOG_TABLE_NAME, null, values);
|
||||
db.close();
|
||||
}
|
||||
|
||||
// select all data
|
||||
public ArrayList<GameLog> readAllData() {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
Cursor cursor = db.query(GAMESLOG_TABLE_NAME, new String[]{"playDate", "playTime", "duration", "winningStatus"}, null, null, null, null, "gameId");
|
||||
ArrayList<GameLog> data = new ArrayList<GameLog>();
|
||||
|
||||
// Time formatter
|
||||
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm aa");
|
||||
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
|
||||
// yyyy-MM-dd striing to date object
|
||||
Date playDate = Date.valueOf(cursor.getString(0));
|
||||
Log.i("db", cursor.getString(1));
|
||||
Time playTime = new Time(timeFormat.parse(cursor.getString(1)).getTime());
|
||||
int duration = cursor.getInt(2);
|
||||
short winningStatus = cursor.getShort(3);
|
||||
|
||||
data.add(new GameLog(playDate, playTime, duration, winningStatus));
|
||||
|
||||
}
|
||||
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
db.close();
|
||||
return data;
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user