diff --git a/AppDocumentation/AppStructure.md b/AppDocumentation/AppStructure.md index a768039..a00d5f2 100644 --- a/AppDocumentation/AppStructure.md +++ b/AppDocumentation/AppStructure.md @@ -4,4 +4,5 @@ ## Auxiliary Tools - https://www.aconvert.com/image/png-to-svg/ +- https://svgtopng.com/ diff --git a/AppDocumentation/AppStructure.puml b/AppDocumentation/AppStructure.puml index 0d435b5..4bcd3e4 100644 --- a/AppDocumentation/AppStructure.puml +++ b/AppDocumentation/AppStructure.puml @@ -1,43 +1,176 @@ @startuml + +'left to right direction +' https://svgtopng.com/ note "Only fields with val, will have 'val' before it, the others are all var\nAnd methods with no indicated visibility are public " as N1 ' -----CONTROLLER----- package Controller <> { - class Chess4AndroidApp{ - + __ fields __ + const val DATEPATTERN: String + -const val TAG: String + -const val DB: String + val historyDB: GamesDataBase + val repo: Chess4AndroidRepo + __ methods __ + override onCreate() + play(id: Int, ctx: Context) + getTodaysDate(): String + log(s: String) + log(t: String, s: String) + toast(s: String, ctx: Context) + toast(id: Int, ctx: Context) + topToast(text: String, ctx: Context) } class Chess4AndroidRepo { - + __ fields __ + const val TAG: String + const val LICHESSDAILYPUZZLEURL: String + private val historyGameDAO: GameTableDAO + __ constructor __ + (historyGameDAO: GameTableDAO) + __ methods __ + -getLatestPuzzleFromDB(callback: (Result) -> Unit) + -saveToDB(dto: GameDTO, callback: (Result) -> Unit = { }) + -getTodaysPuzzleFromAPI(context: Application?, callback: (Result) -> Unit) + getTodaysGame(context: Application?, callback: (Result) -> Unit) + -lichessJSON_to_GameDTO(lichessJSON: LichessJSON) : GameDTO } package MainActivity.kt <> { class MainActivity { + __ fields __ + const val TAG: String + const val DATEFILE: String + const val GAME_DTO_KEY: String + -continueButton: Button? + -val binding: ActivityMainBinding + -val thisViewModel: MainActivityViewModel + -const val IS_GAME_READY_LIVEDATA_KEY + __overrides__ + onCreate(savedInstanceState: Bundle?) + onDestroy() + onBackPressed() + onCreateOptionsMenu(menu: Menu): Boolean + onOptionsItemSelected(item: MenuItem): Boolean + + __ methods __ + -snackBar(stringID: Int) + -launchGame() + } + + Android.Application <|-[#blue]- MainActivity class MainActivityViewModel { + __ fields __ + gameDTO: GameDTO + val isGameReady: LiveData + val context: Application + currentScreenOrientation: MutableLiveData + updateDisplayed: MutableLiveData + -val repo: Chess4AndroidRepo + __ constructor__ + (application: Application, private val state: SavedStateHandle) + __ methods __ + getTodaysGame(callback: (Result) -> Unit) + -isDataNullOrEmpty() : Boolean + -readDateOfTheLatestPuzzlePull() : String + -writeDateOfTheLatestPuzzlePulled(string: String) } + + Android.AndroidViewModel <|-[#green]- MainActivityViewModel } package PuzzleSolvingActivity.kt <> { class PuzzleSolvingActivity { + __ fields __ + -val thisViewModel: PuzzleSolvingActivityViewModel + -lateinit myView: BoardView + -lateinit soloPlaySwitch: SwitchCompat + -lateinit solutionSwitch: SwitchCompat + -lateinit currentColorPlaying: ToggleButton + -currentlySelectedPieceIndex: Int + __ overrides __ + onCreate(savedInstanceState: Bundle?) + onStart() + onResume() + onPause() + onBackPressed() + __ methods __ + -tileBehaviour(tile: Tile) + -moveIt(pieceThatWillBeEatenIndex: Int, pieceToMove: Piece) + -finishedPuzzle() + -snackBar(stringID: Int) + -loadGame() : Boolean + -performSolution() + -invalidateEverything() } + + Android.AppCompatActivity <|-[#red]- PuzzleSolvingActivity class PuzzleSolvingActivityViewModel { + __ fields __ + -val historyDB: GameTableDAO + soloPlay: Boolean + isDone: Boolean + isGameLoaded: Boolean + isWhitesPlaying: MutableLiveData + board: Board + correctMovementsPerformed: Int + gameDTO: GameDTO? + puzzle: Array? + solution: Array? + __ constructor __ + (application: Application) + __ methods __ + setGameAsDoneInDB() } + + Android.AndroidViewModel <|-[#black]- PuzzleSolvingActivityViewModel } package GameHistoryActivity.kt <> { class GameHistoryActivity { + __ fields __ + -val binding: ActivityGameHistoryBinding + -val thisViewModel: GameHistoryViewModel + + __ overrides__ + onCreate(savedInstanceState: Bundle?) + onResume() + onItemClicked(gameDTO: GameDTO, holderPosition: Int) + onCheckBoxClicked(isChecked: Boolean) + __ methods __ + -launchGame(gameDTO: GameDTO) } + + OnItemClickListener <|- GameHistoryActivity class GameHistoryActivityViewModel { + __ fields __ + history: LiveData>? + gameSelected: Int + -val historyDB: GameTableDAO + __ methods __ + loadHistory() : LiveData> } + + Android.AndroidViewModel <|- GameHistoryViewModel + + interface OnItemClickListener{ + onItemClicked(gameDTO: GameDTO, holderPosition: Int) + onCheckBoxClicked(isChecked: Boolean) + } } class AboutActivity { - onCreate(savedInstanceState: Bundle?) + __ fields __ + const val GITHUBURL: String + __ overrides __ + onCreate(savedInstanceState: Bundle?) } } @@ -45,30 +178,85 @@ package Controller <> { - ' -----MODEL---------- package Model <> { - - package asyncUtils.kt <> { - - } + class asyncUtils <<(K,#7B86E2)>> { + -- fields -- + -val ioExecutor: ExecutorService + -val dataAccessExecutor: ExecutorService + -- methods -- + - executeAndCollectResult(asyncAction: () -> T): Result + callbackAfterAsync(callback: (Result) -> Unit, asyncAction: () -> T) + publishInLiveDataAfterAsync(asyncAction: () -> T): LiveData> + doAsyncWithResult(action: () -> T) : MutableLiveData + doAsync(action: () -> Unit) + } + + class LichessJSON <<(K,#7B86E2)>> { + {{json + {"game":{"id": "String","perf": {"icon": "String","name": "String"},"rated": "Boolean","players": [{"userId": "String","name": "String","color": "String"},{"userId": "String","name": "String","color": "String"}],"pgn": "String"}, "puzzle": { + "id": "String", + "rating": "Int", + "plays": "Int", + "initialPly": "Int", + "solution": "Array", + "themes": "Array"}} + }} + } class AutoGetPuzzleWorker { - + __ constructor __ + (appContext: Context, workerParams: WorkerParameters) + __ interface overrides __ + override fun startWork(): ListenableFuture } + Android.ListenableWorker <|-- AutoGetPuzzleWorker + class Board { - + __ fields __ + -const val TAG: String + val BOARD_SIDE_SIZE: Int + val BOARDLENGHT: Int + -val startingChessPiecesTablePositions: Array + -chessPiecesTablePositions: Array + + __ companion object __ + val companion_chessTable: Array + indexToPosition(index: Int) : Position + positionToIndex(position: Position) : Int + -letterToColumnNumber(char: Char) : Int + -columnNumberToLetter(n : Int) : Char + + __ methods __ + -isNotEmptyPiece(index: Int) : Boolean + -isPositionWithPieceType(index: Int, pieceType: PIECETYPE) : Boolean + getPieceAtIndex(index: Int) : Piece + -getPieceAtPosition(position: Position) : Piece + -getPieceColor(position: Position) : Boolean? + -getIndexesOfPieceWithConditions(column: Char?, line: Byte?, pieceType: PIECETYPE?, isWhite: Boolean?) : IntArray + -getIndexOfPieceWithConditions2(column: Char?, line: Char?, pieceType: PIECETYPE?, isWhite: Boolean?) : IntArray + -setPieceAtIndex(index: Int, piece: Piece) + -setPieceAtPosition(position: Position, piece: Piece) + switchPiecesAtIndexes(index1: Int, index2: Int) + movePieceToAndLeaveEmptyBehind(origin: Position, destination: Position) + movePieceToAndLeaveEmptyBehind(indexOrigin: Int, indexDestination: Int) + movePieceToAndLeaveEmptyBehind(indexDestination: Int, pieceOrigin: Piece) + movePieceToAndLeaveEmptyBehind(position: Position, pieceOrigin: Piece) + interpretMove(move: String, isWhite: Boolean) : Boolean + -getPieceThatCanMoveTo(destination: Position, array: IntArray) : Piece? + -isPathClear(origin: Position, destination: Position) : Boolean + -isOutOfBounds (index: Int) : Boolean } package ChessPieces.kt <> { class Position { + -- fields -- + letter: Char + number: Byte __ constructors __ (letter: Char, number: Byte) (string: String) - -- fields -- - letter: Char - number: Byte -- methods -- -isValid() -isValid(string: String) : Boolean @@ -85,15 +273,15 @@ package Model <> { } abstract class Piece { + -- fields -- + position: Position + isWhite: Boolean + {abstract} abstract pieceType: PIECETYPE + {abstract} abstract val maxTravelDistanceX: Byte + {abstract} abstract val maxTravelDistanceY: Byte __ constructors __ (position: Position, isWhite: Boolean) (letter: Char, number: Byte, isWhite: Boolean) - -- fields -- - position: Position - isWhite: Boolean - {abstract} abstract pieceType: PIECETYPE - {abstract} abstract val maxTravelDistanceX: Byte - {abstract} abstract val maxTravelDistanceY: Byte -- methods -- {abstract} abstract canMoveTo(destination: Position): Boolean } @@ -153,29 +341,42 @@ package Model <> { } class GameDTO { - + __ fields __ + id: String? + puzzle: String? + solution: String? + date: String? + isDone: Boolean + __ methods __ + toGameTable() : GameTable } package GameHistoryDB.kt <> { class GameTable { - + @PrimaryKey id: String + puzzle: String + solution: String + date: String + isDone: Boolean + __ methods __ + toGameDTO() : GameDTO } interface GameTableDAO { - + @Insert insert(gameTable: GameTable) + @Delete delete(gameTable: GameTable) + @Update update(gameTable: GameTable) + @Query getAll() : List + @Query getLast(count: Int) : List + @Query getGameWithID(id: String) : GameTable + @Query setIsDone(id: String, isDone: Boolean) } abstract class GamesDataBase { - + {abstract} abstract getDAO() : GameTableDAO } } - - package LichessJSON.kt <> { - - - } - } @@ -187,19 +388,85 @@ package Model <> { package Views <> { class BoardView { - + __ fields __ + const val SIDE: Int + const val TAG: String + tileMatrix: Array + -val ctx: Context + -val blankIcon: VectorDrawableCompat + __ constructor __ + (ctx: Context, attrs: AttributeSet?) + __ methods __ + -indexToColumn(it: Int): VectorDrawableCompat? + -getDrawablePiece(pieceType: PIECETYPE, isWhite: Boolean) : VectorDrawableCompat? + invalidate(index: Int, piece: Piece) + -getIcon(xmlID: Int): VectorDrawableCompat? + -val brush: Paint + override dispatchDraw(canvas: Canvas) } - package GameHistoryViewAdapter.kt <> { + Android.GridLayout <|-- BoardView + + package GameHistoryViewAdapter.kt <> { class GameHistoryViewAdapter { + -gamesHistoryData: List + -itemClickedListener: OnItemClickListener + __ overrides __ + onCreateViewHolder(parent: ViewGroup, viewType: Int): HistoryItemViewHolder + onBindViewHolder(holder: HistoryItemViewHolder, position: Int) + getItemCount(): Int } + + Android.RecyclerView_Adapter <|-[#orange]- GameHistoryViewAdapter + class HistoryItemViewHolder { + __ fields __ + -val dateView: TextView + -val idView: TextView + -val checkBox: CheckBox + __ constructor __ + (itemView: View) + __ methods __ + bindTo(gameDTO: GameDTO, itemClickedListener: OnItemClickListener) } + + Android.RecyclerView_ViewHolder <|-[#black]- HistoryItemViewHolder } class Tile { - + __ fields __ + ctx: Context + val isWhite: Boolean? + val tilesPerSide: Int + icon: VectorDrawableCompat + letter: VectorDrawableCompat? + number: VectorDrawableCompat? + index: Int + -val padding: Int + -val brush: Paint + __ constructor__ + (ctx: Context, val isWhite: Boolean?, val tilesPerSide: Int, var icon: VectorDrawableCompat,letter: VectorDrawableCompat?,number: VectorDrawableCompat?, index: Int) + __ overrides __ + onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) + fun onDraw(canvas: Canvas) + __ methods __ + setIcon (icon: VectorDrawableCompat) : Tile } + Android.View <|-[#yellow]- Tile + +} + + + +package Android <> { + interface ListenableWorker + class AppCompatActivity + class Application + class AndroidViewModel + class GridLayout + class RecyclerView_Adapter + class RecyclerView_ViewHolder + class View } @enduml \ No newline at end of file diff --git a/AppDocumentation/AppStructure__.png b/AppDocumentation/AppStructure__.png new file mode 100644 index 0000000..3ea2854 Binary files /dev/null and b/AppDocumentation/AppStructure__.png differ diff --git a/AppDocumentation/AppStructure__Controller_Horizontal.png b/AppDocumentation/AppStructure__Controller_Horizontal.png new file mode 100644 index 0000000..e8c3f8f Binary files /dev/null and b/AppDocumentation/AppStructure__Controller_Horizontal.png differ diff --git a/AppDocumentation/AppStructure__Controller_Vertical.png b/AppDocumentation/AppStructure__Controller_Vertical.png new file mode 100644 index 0000000..6396da5 Binary files /dev/null and b/AppDocumentation/AppStructure__Controller_Vertical.png differ diff --git a/AppDocumentation/AppStructure__Model.png b/AppDocumentation/AppStructure__Model.png new file mode 100644 index 0000000..639a15e Binary files /dev/null and b/AppDocumentation/AppStructure__Model.png differ diff --git a/AppDocumentation/AppStructure__View.png b/AppDocumentation/AppStructure__View.png new file mode 100644 index 0000000..0da2abf Binary files /dev/null and b/AppDocumentation/AppStructure__View.png differ diff --git a/Chess4Android/.gradle/7.2/executionHistory/executionHistory.bin b/Chess4Android/.gradle/7.2/executionHistory/executionHistory.bin index 656f44f..1e2f116 100644 Binary files a/Chess4Android/.gradle/7.2/executionHistory/executionHistory.bin and b/Chess4Android/.gradle/7.2/executionHistory/executionHistory.bin differ diff --git a/Chess4Android/.gradle/7.2/executionHistory/executionHistory.lock b/Chess4Android/.gradle/7.2/executionHistory/executionHistory.lock index f298dde..7aaf1ce 100644 Binary files a/Chess4Android/.gradle/7.2/executionHistory/executionHistory.lock and b/Chess4Android/.gradle/7.2/executionHistory/executionHistory.lock differ diff --git a/Chess4Android/.gradle/7.2/fileHashes/fileHashes.bin b/Chess4Android/.gradle/7.2/fileHashes/fileHashes.bin index e75ea7f..fe0589c 100644 Binary files a/Chess4Android/.gradle/7.2/fileHashes/fileHashes.bin and b/Chess4Android/.gradle/7.2/fileHashes/fileHashes.bin differ diff --git a/Chess4Android/.gradle/7.2/fileHashes/fileHashes.lock b/Chess4Android/.gradle/7.2/fileHashes/fileHashes.lock index 4d97a8d..59c2d4a 100644 Binary files a/Chess4Android/.gradle/7.2/fileHashes/fileHashes.lock and b/Chess4Android/.gradle/7.2/fileHashes/fileHashes.lock differ diff --git a/Chess4Android/.gradle/7.2/fileHashes/resourceHashesCache.bin b/Chess4Android/.gradle/7.2/fileHashes/resourceHashesCache.bin index 4eaf33c..26b9863 100644 Binary files a/Chess4Android/.gradle/7.2/fileHashes/resourceHashesCache.bin and b/Chess4Android/.gradle/7.2/fileHashes/resourceHashesCache.bin differ diff --git a/Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index c43bb15..c325b44 100644 Binary files a/Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/Chess4Android/.idea/misc.xml b/Chess4Android/.idea/misc.xml index cdaecb9..d9088c7 100644 --- a/Chess4Android/.idea/misc.xml +++ b/Chess4Android/.idea/misc.xml @@ -46,6 +46,7 @@ + diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/Chess4AndroidRepo.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/Chess4AndroidRepo.kt index 8601aa3..8518844 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/Chess4AndroidRepo.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/Chess4AndroidRepo.kt @@ -16,6 +16,7 @@ import pt.isel.pdm.chess4android.model.* */ private const val TAG = "Repo" +const val LICHESSDAILYPUZZLEURL: String = "https://lichess.org/api/puzzle/daily" class Chess4AndroidRepo(private val historyGameDAO: GameTableDAO) { @@ -30,12 +31,13 @@ class Chess4AndroidRepo(private val historyGameDAO: GameTableDAO) { historyGameDAO.insert(dto.toGameTable()) } } + private fun getTodaysPuzzleFromAPI(context: Application?, callback: (Result) -> Unit){ val queue = Volley.newRequestQueue(context) val responseListener = Response.Listener { response -> log(response.toString()) val lichessGameOfTheDay = Json { ignoreUnknownKeys = true }.decodeFromString(LichessJSON.serializer(),response) - val result = Result.success(LichessJSON_to_GameDTO(lichessGameOfTheDay)) + val result = Result.success(lichessJSON_to_GameDTO(lichessGameOfTheDay)) callback(result) log("Response received") } @@ -54,22 +56,22 @@ class Chess4AndroidRepo(private val historyGameDAO: GameTableDAO) { getLatestPuzzleFromDB { maybeEntity -> val maybeGame = maybeEntity.getOrNull() if (maybeGame?.date==getTodaysDate()) { - log(TAG, "Thread ${Thread.currentThread().name}: Got daily quote from local DB") + log(TAG, "Thread ${Thread.currentThread().name}: Got daily puzzle from local DB") callback(Result.success(maybeGame.toGameDTO())) } else { getTodaysPuzzleFromAPI(context) { apiResult -> apiResult.onSuccess { gameDTO -> - log(TAG, "Thread ${Thread.currentThread().name}: Got daily quote from API") + log(TAG, "Thread ${Thread.currentThread().name}: Got daily puzzle from API") saveToDB(gameDTO!!) { saveToDBResult -> saveToDBResult.onSuccess { - log(TAG, "Thread ${Thread.currentThread().name}: Saved daily quote to local DB") + log(TAG, "Thread ${Thread.currentThread().name}: Saved daily puzzle to local DB") callback(Result.success(gameDTO)) } - .onFailure { - Log.i(TAG, "Thread ${Thread.currentThread().name}: Failed to save daily quote to local DB", it) - callback(Result.failure(it)) - } + .onFailure { + Log.i(TAG, "Thread ${Thread.currentThread().name}: Failed to save daily puzzle to local DB", it) + callback(Result.failure(it)) + } } } callback(apiResult) @@ -79,7 +81,7 @@ class Chess4AndroidRepo(private val historyGameDAO: GameTableDAO) { } } -fun LichessJSON_to_GameDTO(lichessJSON: LichessJSON) : GameDTO { +private fun lichessJSON_to_GameDTO(lichessJSON: LichessJSON) : GameDTO { val sb: StringBuilder = StringBuilder() for(i in lichessJSON.puzzle.solution){ sb.append("$i ") diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/GameHistoryActivity.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/GameHistoryActivity.kt index a872af3..176edd6 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/GameHistoryActivity.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/GameHistoryActivity.kt @@ -71,7 +71,7 @@ class GameHistoryViewModel(application: Application) : AndroidViewModel(applicat var gameSelected: Int = -1 - private val historyDB : GameTableDAO by lazy { + private val historyDB: GameTableDAO by lazy { getApplication().historyDB.getDAO() } diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/MainActivity.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/MainActivity.kt index bd31c5f..6dcc31a 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/MainActivity.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/MainActivity.kt @@ -22,7 +22,6 @@ import pt.isel.pdm.chess4android.databinding.ActivityMainBinding import pt.isel.pdm.chess4android.model.* private const val TAG = "MainActivity" -const val LICHESSDAILYPUZZLEURL: String = "https://lichess.org/api/puzzle/daily" private const val DATEFILE = "latest_data_fetch_date.txt" //LiveData and Intent data keys const val GAME_DTO_KEY = "game" @@ -166,7 +165,7 @@ class MainActivityViewModel(application: Application, private val state: SavedSt } } - fun isDataNullOrEmpty() = gameDTO?.id.isNullOrEmpty() || gameDTO?.puzzle.isNullOrEmpty() || gameDTO?.solution.isNullOrEmpty() || gameDTO?.date.isNullOrEmpty() + private fun isDataNullOrEmpty() = gameDTO?.id.isNullOrEmpty() || gameDTO?.puzzle.isNullOrEmpty() || gameDTO?.solution.isNullOrEmpty() || gameDTO?.date.isNullOrEmpty() //Current date methods diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/PuzzleSolvingActivity.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/PuzzleSolvingActivity.kt index 0d659a8..bee6625 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/PuzzleSolvingActivity.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/PuzzleSolvingActivity.kt @@ -11,7 +11,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.SwitchCompat import androidx.lifecycle.MutableLiveData import com.google.android.material.snackbar.Snackbar -import pt.isel.pdm.chess4android.databinding.ActivityMainBinding import pt.isel.pdm.chess4android.model.* import pt.isel.pdm.chess4android.views.BoardView import pt.isel.pdm.chess4android.views.Tile @@ -72,7 +71,7 @@ class PuzzleSolvingActivity : AppCompatActivity() { if(thisViewModel.isGameLoaded){ invalidateEverything() } else if(loadGame()) { - invalidateEverything() //it's easier for us to invalidate everything when loading + invalidateEverything() //it's easier for us to invalidate everything after all the pieces are set, instead of invalidating for every move. Every puzzle has 30+ moves. So that's 30*2 tile invalidates. So, its worth the simplicity (or cost) of invalidating everything (64 positions) play(R.raw.pictures_snare, this) } } @@ -107,8 +106,8 @@ class PuzzleSolvingActivity : AppCompatActivity() { log("analysing movement validity:") val pieceToMove = thisViewModel.board.getPieceAtIndex(currentlySelectedPieceIndex) val pieceThatWillBeEatenIndex = tile.index - val thePieceToBeEaten = thisViewModel.board.getPieceAtIndex(pieceThatWillBeEatenIndex) - val theNewPosition = thePieceToBeEaten.position + val pieceThatWillBeEaten = thisViewModel.board.getPieceAtIndex(pieceThatWillBeEatenIndex) + val theNewPosition = pieceThatWillBeEaten.position log("destination has index = $pieceThatWillBeEatenIndex and position = $theNewPosition ") if(pieceThatWillBeEatenIndex!=currentlySelectedPieceIndex) { @@ -116,7 +115,7 @@ class PuzzleSolvingActivity : AppCompatActivity() { if(pieceToMove.canMoveTo(theNewPosition)){ if(pieceToMove.pieceType==PIECETYPE.PAWN) { val thePawn = pieceToMove as ChessPieces.Pawn - if(thePawn.movesDiagonally(theNewPosition) && thePieceToBeEaten.pieceType==PIECETYPE.EMPTY){ + if(thePawn.movesDiagonally(theNewPosition) && pieceThatWillBeEaten.pieceType==PIECETYPE.EMPTY){ log("the pawn can only move diagonally when it will eat a piece") } else moveIt(pieceThatWillBeEatenIndex, pieceToMove) } else { @@ -124,7 +123,7 @@ class PuzzleSolvingActivity : AppCompatActivity() { } } else log("the piece cant move to selected position") } else log("the pieces are of the same color!") - } else log("the indexes of the pieces to move are the same or some values are null") + } else log("the indexes of the pieces to move are the same") currentlySelectedPieceIndex = -1 } } @@ -219,7 +218,7 @@ class PuzzleSolvingActivity : AppCompatActivity() { //if(index==14) return true //useful for testing index by index, movement by movement } thisViewModel.isWhitesPlaying.value = !isWhitesPlaying - toast(R.string.loadSuccess, this) + // toast(R.string.loadSuccess, this) thisViewModel.isGameLoaded = true return true } @@ -244,7 +243,7 @@ class PuzzleSolvingActivity : AppCompatActivity() { } } -class PuzzleSolvingActivityViewModel(application: Application, private val state: SavedStateHandle) : AndroidViewModel(application) { +class PuzzleSolvingActivityViewModel(application: Application) : AndroidViewModel(application) { init { log("MainActivityViewModel.init()") } diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/Board.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/Board.kt index 0f1293e..ecb7ac3 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/Board.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/Board.kt @@ -1,6 +1,5 @@ package pt.isel.pdm.chess4android.model -import android.util.Log import pt.isel.pdm.chess4android.log import kotlin.math.abs private const val TAG = "Board" @@ -10,92 +9,93 @@ const val BOARDLENGHT: Int = BOARD_SIDE_SIZE * BOARD_SIDE_SIZE class Board { private val startingChessPiecesTablePositions = arrayOf( - ChessPieces.Rook('a', 8, false), - ChessPieces.Knight('b', 8, false), - ChessPieces.Bishop('c', 8, false), - ChessPieces.Queen('d', 8,false), - ChessPieces.King('e', 8, false), - ChessPieces.Bishop('f', 8, false), - ChessPieces.Knight('g', 8, false), - ChessPieces.Rook('h', 8, false), - - ChessPieces.Pawn('a', 7, false), - ChessPieces.Pawn('b', 7, false), - ChessPieces.Pawn('c', 7, false), - ChessPieces.Pawn('d', 7, false), - ChessPieces.Pawn('e', 7, false), - ChessPieces.Pawn('f', 7, false), - ChessPieces.Pawn('g', 7, false), - ChessPieces.Pawn('h', 7, false), - - ChessPieces.Empty('a', 6), - ChessPieces.Empty('b', 6), - ChessPieces.Empty('c', 6), - ChessPieces.Empty('d', 6), - ChessPieces.Empty('e', 6), - ChessPieces.Empty('f', 6), - ChessPieces.Empty('g', 6), - ChessPieces.Empty('h', 6), - - ChessPieces.Empty('a', 5), - ChessPieces.Empty('b', 5), - ChessPieces.Empty('c', 5), - ChessPieces.Empty('d', 5), - ChessPieces.Empty('e', 5), - ChessPieces.Empty('f', 5), - ChessPieces.Empty('g', 5), - ChessPieces.Empty('h', 5), - - ChessPieces.Empty('a', 4), - ChessPieces.Empty('b', 4), - ChessPieces.Empty('c', 4), - ChessPieces.Empty('d', 4), - ChessPieces.Empty('e', 4), - ChessPieces.Empty('f', 4), - ChessPieces.Empty('g', 4), - ChessPieces.Empty('h', 4), - - ChessPieces.Empty('a', 3), - ChessPieces.Empty('b', 3), - ChessPieces.Empty('c', 3), - ChessPieces.Empty('d', 3), - ChessPieces.Empty('e', 3), - ChessPieces.Empty('f', 3), - ChessPieces.Empty('g', 3), - ChessPieces.Empty('h', 3), - - ChessPieces.Pawn('a', 2, true), - ChessPieces.Pawn('b', 2, true), - ChessPieces.Pawn('c', 2, true), - ChessPieces.Pawn('d', 2, true), - ChessPieces.Pawn('e', 2, true), - ChessPieces.Pawn('f', 2, true), - ChessPieces.Pawn('g', 2, true), - ChessPieces.Pawn('h', 2, true), - - ChessPieces.Rook('a', 1, true), - ChessPieces.Knight('b', 1, true), - ChessPieces.Bishop('c', 1, true), - ChessPieces.Queen('d', 1,true), - ChessPieces.King('e', 1, true), - ChessPieces.Bishop('f', 1, true), - ChessPieces.Knight('g', 1, true), - ChessPieces.Rook('h', 1, true) + ChessPieces.Rook(Position('a', 8), false), + ChessPieces.Knight(Position('b', 8), false), + ChessPieces.Bishop(Position('c', 8), false), + ChessPieces.Queen(Position('d', 8),false), + ChessPieces.King(Position('e', 8), false), + ChessPieces.Bishop(Position('f', 8), false), + ChessPieces.Knight(Position('g', 8), false), + ChessPieces.Rook(Position('h', 8), false), + + ChessPieces.Pawn(Position('a', 7), false), + ChessPieces.Pawn(Position('b', 7), false), + ChessPieces.Pawn(Position('c', 7), false), + ChessPieces.Pawn(Position('d', 7), false), + ChessPieces.Pawn(Position('e', 7), false), + ChessPieces.Pawn(Position('f', 7), false), + ChessPieces.Pawn(Position('g', 7), false), + ChessPieces.Pawn(Position('h', 7), false), + + ChessPieces.Empty(Position('a', 6)), + ChessPieces.Empty(Position('b', 6)), + ChessPieces.Empty(Position('c', 6)), + ChessPieces.Empty(Position('d', 6)), + ChessPieces.Empty(Position('e', 6)), + ChessPieces.Empty(Position('f', 6)), + ChessPieces.Empty(Position('g', 6)), + ChessPieces.Empty(Position('h', 6)), + + ChessPieces.Empty(Position('a', 5)), + ChessPieces.Empty(Position('b', 5)), + ChessPieces.Empty(Position('c', 5)), + ChessPieces.Empty(Position('d', 5)), + ChessPieces.Empty(Position('e', 5)), + ChessPieces.Empty(Position('f', 5)), + ChessPieces.Empty(Position('g', 5)), + ChessPieces.Empty(Position('h', 5)), + + ChessPieces.Empty(Position('a', 4)), + ChessPieces.Empty(Position('b', 4)), + ChessPieces.Empty(Position('c', 4)), + ChessPieces.Empty(Position('d', 4)), + ChessPieces.Empty(Position('e', 4)), + ChessPieces.Empty(Position('f', 4)), + ChessPieces.Empty(Position('g', 4)), + ChessPieces.Empty(Position('h', 4)), + + ChessPieces.Empty(Position('a', 3)), + ChessPieces.Empty(Position('b', 3)), + ChessPieces.Empty(Position('c', 3)), + ChessPieces.Empty(Position('d', 3)), + ChessPieces.Empty(Position('e', 3)), + ChessPieces.Empty(Position('f', 3)), + ChessPieces.Empty(Position('g', 3)), + ChessPieces.Empty(Position('h', 3)), + + ChessPieces.Pawn(Position('a', 2), true), + ChessPieces.Pawn(Position('b', 2), true), + ChessPieces.Pawn(Position('c', 2), true), + ChessPieces.Pawn(Position('d', 2), true), + ChessPieces.Pawn(Position('e', 2), true), + ChessPieces.Pawn(Position('f', 2), true), + ChessPieces.Pawn(Position('g', 2), true), + ChessPieces.Pawn(Position('h', 2), true), + + ChessPieces.Rook(Position('a', 1), true), + ChessPieces.Knight(Position('b', 1), true), + ChessPieces.Bishop(Position('c', 1), true), + ChessPieces.Queen(Position('d', 1),true), + ChessPieces.King(Position('e', 1), true), + ChessPieces.Bishop(Position('f', 1), true), + ChessPieces.Knight(Position('g', 1), true), + ChessPieces.Rook(Position('h', 1), true) ) + private var chessPiecesTablePositions = startingChessPiecesTablePositions.copyOf() companion object { val companion_chessTable = Board().startingChessPiecesTablePositions.copyOf() //startingChessPiecesTablePositions will be read only for classes that want to acess it fun indexToPosition(index: Int) : Position { - return Position(numberToLetter(index % BOARD_SIDE_SIZE), (BOARD_SIDE_SIZE / 8).toByte()) + return Position(columnNumberToLetter(index % BOARD_SIDE_SIZE), (BOARD_SIDE_SIZE / 8).toByte()) } fun positionToIndex(position: Position) : Int { //log("position->$position") - val res : String = ((BOARD_SIDE_SIZE-position.number) * BOARD_SIDE_SIZE + letterToColumn(position.letter)).toString() + //val res : String = ((BOARD_SIDE_SIZE-position.number) * BOARD_SIDE_SIZE + letterToColumnNumber(position.letter)).toString() //log("to index -> $res") - return (BOARD_SIDE_SIZE-position.number) * BOARD_SIDE_SIZE + letterToColumn(position.letter) + return (BOARD_SIDE_SIZE-position.number) * BOARD_SIDE_SIZE + letterToColumnNumber(position.letter) } - private fun letterToColumn(char: Char) : Int { + private fun letterToColumnNumber(char: Char) : Int { return when(char){ 'a' -> 0 'b' -> 1 @@ -109,7 +109,7 @@ class Board { } } - private fun numberToLetter(n : Int) : Char { + private fun columnNumberToLetter(n : Int) : Char { return when(n){ 0 -> 'a' 1 -> 'b' @@ -123,8 +123,8 @@ class Board { } } } - private var chessPiecesTablePositions = startingChessPiecesTablePositions.copyOf() - constructor() { + + init { assert(chessPiecesTablePositions.size == BOARDLENGHT) } @@ -133,7 +133,7 @@ class Board { // BOOLEANS private fun isNotEmptyPiece(index: Int) : Boolean = getPieceAtIndex(index).pieceType!=PIECETYPE.EMPTY - fun isPositionWithPieceType(index: Int, pieceType: PIECETYPE) : Boolean { + private fun isPositionWithPieceType(index: Int, pieceType: PIECETYPE) : Boolean { if(getPieceAtIndex(index).pieceType==pieceType) return true return false } @@ -144,6 +144,15 @@ class Board { return chessPiecesTablePositions[index] } + private fun getPieceAtPosition(position: Position) : Piece = getPieceAtIndex(positionToIndex(position)) + + private fun getPieceColor(position: Position) : Boolean? { + val piece = getPieceAtPosition(position) + if(piece.pieceType==PIECETYPE.EMPTY) return null + if(piece.isWhite) return true + return false + } + // GET INDEX (and thus piece, and its properties), also can serve as method as isPositionWithPieceType : Boolean, per example // function use in a nutshell: if param is null, dont evaluate equality when searching for the index, otherwise, do evaluate. @@ -151,23 +160,28 @@ class Board { var boolColumn = column!=null var boolLine = line!=null var boolPosition = boolColumn && boolLine + if(boolPosition) { //to null out duplicate checking of position AND column and line + boolColumn = false + boolLine = false + } var boolType = pieceType!=null var boolIsWhite = isWhite!=null var position: Position? = null - var arrayOfMaxingIndexes = IntArray(BOARD_SIDE_SIZE) { -1 } + var arrayOfMatchingIndexes = IntArray(BOARD_SIDE_SIZE*2) { -1 } //this is the length considering that since the color is always specified in our code, it can be 8*2. For the worst and possibly impossible case, all pieces could go to some position, which would require setting the lenght to (8*4) + if(!boolColumn && !boolLine && !boolPosition && !boolType && !boolIsWhite) return arrayOfMatchingIndexes if(boolPosition){ //is position really needed? if we have both line and column? try { position = Position(column!!, line!!) //according to our validation above, !! is fine and has to be here boolColumn = false boolLine = false } catch (e: IllegalArgumentException){ - return arrayOfMaxingIndexes + return arrayOfMatchingIndexes } } else if(boolColumn){ - if(!validXPositions.contains(column!!)) return arrayOfMaxingIndexes + if(!validXPositions.contains(column!!)) return arrayOfMatchingIndexes } else if(boolLine){ - if(!validYPositions.contains(line!!)) return arrayOfMaxingIndexes + if(!validYPositions.contains(line!!)) return arrayOfMatchingIndexes } var i = -1 @@ -189,9 +203,9 @@ class Board { if(boolIsWhite){ if(piece.isWhite!=isWhite) continue } - arrayOfMaxingIndexes[index++] = i + arrayOfMatchingIndexes[index++] = i } - return arrayOfMaxingIndexes + return arrayOfMatchingIndexes } // I had to add 2 to the end of the method, because if there are certain params that are null the compiler cannot know which overloaded method is to be called @@ -203,7 +217,7 @@ class Board { chessPiecesTablePositions[index]=piece } - fun setPieceAtPosition(position: Position, piece: Piece) = setPieceAtIndex(positionToIndex(position), piece) + private fun setPieceAtPosition(position: Position, piece: Piece) = setPieceAtIndex(positionToIndex(position), piece) // *** MOVEMENTS *** @@ -234,12 +248,12 @@ class Board { val auxPosition = pieceOrigin.position pieceOrigin.position = getPieceAtIndex(indexDestination)?.position!! //change the position of the piece to the position of the destination that its going to (change the value the object has) setPieceAtIndex(indexDestination, pieceOrigin) //change the array at the index of destination (change the positions at which the objects are located in the array) - setPieceAtIndex(positionToIndex(auxPosition), ChessPieces.Empty(auxPosition.letter, auxPosition.number)) //change + setPieceAtIndex(positionToIndex(auxPosition), ChessPieces.Empty(Position(auxPosition.letter, auxPosition.number))) //change } private fun movePieceToAndLeaveEmptyBehind(position: Position, pieceOrigin: Piece) = movePieceToAndLeaveEmptyBehind(positionToIndex(position), pieceOrigin) - /* + /** * examples: * pawns: e4, exf5 * others: Nf3, Nxg5, Nfg5 (on "conflict" of possibilities), Nfxg5 (on "conflict" of possibilities AND kills piece) @@ -295,6 +309,7 @@ class Board { if (position != null) { val thePawn: ChessPieces.Pawn? = getPieceThatCanMoveTo(position, getIndexOfPieceWithConditions2(move[0], null, PIECETYPE.PAWN, isWhite)) as? ChessPieces.Pawn //this works for both x and non x cases because the first letter is always the column if (thePawn != null) { + if(thePawn.isWhite==getPieceColor(position)) return false movePieceToAndLeaveEmptyBehind(position, thePawn) return true } @@ -321,6 +336,7 @@ class Board { if(position != null) { val thePiece = pieceToChessPieceCorrespondingToItsType(getPieceThatCanMoveTo(position, getIndexOfPieceWithConditions2(column, line, pieceTypeToMove, isWhite)), pieceTypeToMove) if(thePiece!=null) { + if(thePiece.isWhite==getPieceColor(position)) return false movePieceToAndLeaveEmptyBehind(position, thePiece) return true } diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/ChessPieces.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/ChessPieces.kt index d6d5deb..6568639 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/ChessPieces.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/ChessPieces.kt @@ -1,6 +1,5 @@ package pt.isel.pdm.chess4android.model -import android.util.Log import pt.isel.pdm.chess4android.log import java.lang.IllegalArgumentException import kotlin.math.abs @@ -15,20 +14,17 @@ data class Position(var letter: Char, var number: Byte) { constructor(string: String) : this(string[0], string[1].digitToInt()?.toByte()) private fun isValid() = validXPositions.contains(/*this.*/letter) && validYPositions.contains(/*this.*/number) - private fun isValid(string: String) : Boolean { //probably useless, but if needed, I would need to include O, -, +, # and other special chars - string.forEach { c -> - if(c.isDigit()){ - if(!validYPositions.contains(c.code.toByte())) return false - } - else if(!validXPositions.contains(c)) return false - } - return true + private fun isValid(string: String) : Boolean { + if(string.length!=2) return false + if(validXPositions.contains(string[0]) && validYPositions.contains(string[1].code.toByte())) return true + return false } fun horizontalyInvertPosition() : Position { number = (BOARD_SIDE_SIZE+1 - number).toByte() //https://stackoverflow.com/questions/16242259/reverse-number-in-a-range return this } + override fun toString(): String = "Letter: $letter, Number: $number" fun letterAndNumber() : String ="$letter$number" fun isEqual(position: Position) : Boolean = this.letter==position.letter && this.number==position.number @@ -51,9 +47,8 @@ data class Position(var letter: Char, var number: Byte) { fun convertToPosition(string: String) : Position? { if(string.length==2){ try { - val position = Position(string[0], string[1].digitToInt().toByte()) - return position - } catch (e: Exception){ + return Position(string[0], string[1].digitToInt().toByte()) + } catch (e: Exception) { log(TAG, e.toString()) } } @@ -62,7 +57,7 @@ data class Position(var letter: Char, var number: Byte) { } } -abstract class Piece (var position: Position, open var isWhite: Boolean) { +abstract class Piece (open var position: Position, open var isWhite: Boolean) { abstract val pieceType: PIECETYPE //lowercase abstract val maxTravelDistanceX: Byte //positive value abstract val maxTravelDistanceY: Byte //positive value @@ -73,7 +68,7 @@ abstract class Piece (var position: Position, open var isWhite: Boolean) { sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //maybe not needed here? - data class Pawn (var letter: Char, var number: Byte, override var isWhite: Boolean) : Piece(letter, number, isWhite) { + data class Pawn (override var position: Position, override var isWhite: Boolean) : Piece(position, isWhite) { override val pieceType = PIECETYPE.PAWN override val maxTravelDistanceX: Byte = 1 override val maxTravelDistanceY: Byte = 2 @@ -83,10 +78,10 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m override fun canMoveTo(destination: Position) : Boolean { if(position.isValidMovement(destination, maxTravelDistanceX, maxTravelDistanceY)){ //part checks logical board bounds and piece maxTravelDistance bounds if(!firstMoveUsed && isWhite && position.getYDiferenceNoAbs(destination)==-1 || position.getYDiferenceNoAbs(destination)==-2){ //kotlin ranges doesnt work with negative values... https://kotlinlang.org/docs/ranges.html - //firstMoveUsed = true + if(!firstMoveUsed) firstMoveUsed = true return true } else if (!firstMoveUsed && !isWhite && (position.getYDiferenceNoAbs(destination) in 1..2)){ - //firstMoveUsed = true + if(!firstMoveUsed) firstMoveUsed = true return true } else if(isWhite && position.getYDiferenceNoAbs(destination) == -1){ return true @@ -99,11 +94,10 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m fun movesDiagonally(destination: Position) : Boolean = position.getXDiference(destination)==1 - override fun toString(): String = "Pawn" } - data class Bishop (var letter: Char, var number: Byte, override var isWhite: Boolean) : Piece(letter, number, isWhite) { + data class Bishop (override var position: Position, override var isWhite: Boolean) : Piece(position, isWhite) { override val pieceType = PIECETYPE.BISHOP override val maxTravelDistanceX: Byte = 7 override val maxTravelDistanceY: Byte = 7 @@ -120,7 +114,7 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m override fun toString(): String = "Bishop" } - data class Knight (var letter: Char, var number: Byte, override var isWhite: Boolean) : Piece(letter, number, isWhite) { + data class Knight (override var position: Position, override var isWhite: Boolean) : Piece(position, isWhite) { override val pieceType = PIECETYPE.KNIGHT override val maxTravelDistanceX: Byte = 0 //Knight is an exception (we will do it hardcoded) override val maxTravelDistanceY: Byte = 0 //Knight is an exception (we will do it hardcoded) @@ -138,7 +132,7 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m override fun toString(): String = "Knight" } - data class Rook (var letter: Char, var number: Byte, override var isWhite: Boolean) : Piece(letter, number, isWhite) { + data class Rook (override var position: Position, override var isWhite: Boolean) : Piece(position, isWhite) { override val pieceType = PIECETYPE.ROOK override val maxTravelDistanceX: Byte = 7 override val maxTravelDistanceY: Byte = 7 @@ -148,9 +142,11 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m override fun canMoveTo(destination: Position) : Boolean { if(position.isValidMovement(destination, maxTravelDistanceX, maxTravelDistanceY)){ if(position.getXDiference(destination)==0 && position.getYDiference(destination)!=0) { + if(!firstMoveUsed) firstMoveUsed = true return true } else if (position.getXDiference(destination)!=0 && position.getYDiference(destination)==0) { + if(!firstMoveUsed) firstMoveUsed = true return true } } @@ -160,7 +156,7 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m override fun toString(): String = "Rook" } - data class King (var letter: Char, var number: Byte, override var isWhite: Boolean) : Piece(letter, number, isWhite) { + data class King (override var position: Position, override var isWhite: Boolean) : Piece(position, isWhite) { override val pieceType = PIECETYPE.KING override val maxTravelDistanceX: Byte = 1 override val maxTravelDistanceY: Byte = 1 @@ -169,6 +165,7 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m override fun canMoveTo(destination: Position) : Boolean { if(position.isValidMovement(destination, maxTravelDistanceX, maxTravelDistanceY)){ + if(!firstMoveUsed) firstMoveUsed = true return true } return false @@ -177,7 +174,7 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m override fun toString(): String = "King" } - data class Queen (var letter: Char, var number: Byte, override var isWhite: Boolean) : Piece(letter, number, isWhite) { + data class Queen (override var position: Position, override var isWhite: Boolean) : Piece(position, isWhite) { override val pieceType = PIECETYPE.QUEEN override val maxTravelDistanceX: Byte = 7 override val maxTravelDistanceY: Byte = 7 @@ -192,7 +189,7 @@ sealed class ChessPieces { //https://antonioleiva.com/sealed-classes-kotlin/ //m override fun toString(): String = "Queen" } - data class Empty (var letter: Char, var number: Byte) : Piece(letter, number, false) { + data class Empty (override var position: Position) : Piece(position, false) { override val pieceType = PIECETYPE.EMPTY override val maxTravelDistanceX: Byte = 0 override val maxTravelDistanceY: Byte = 0 diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/views/BoardView.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/views/BoardView.kt index 226a0ca..e419461 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/views/BoardView.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/views/BoardView.kt @@ -9,6 +9,7 @@ import android.util.Log import android.widget.GridLayout import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import pt.isel.pdm.chess4android.R +import pt.isel.pdm.chess4android.log import pt.isel.pdm.chess4android.model.BOARD_SIDE_SIZE import pt.isel.pdm.chess4android.model.Board import pt.isel.pdm.chess4android.model.PIECETYPE @@ -20,21 +21,22 @@ import pt.isel.pdm.chess4android.model.Piece /** * Custom view that implements a chess board. */ -const val side = BOARD_SIDE_SIZE -var tileMatrix = arrayOfNulls(side*side) +const val SIDE = BOARD_SIDE_SIZE +const val TAG = "BoardView" +var tileMatrix = arrayOfNulls(SIDE*SIDE) @SuppressLint("ClickableViewAccessibility") class BoardView(private val ctx: Context, attrs: AttributeSet?) : GridLayout(ctx, attrs) { private val blankIcon = VectorDrawableCompat.create(ctx.resources, R.drawable.ic_blank, null) init { //will always read the startingChessPiecesTablePositions (because the companion object references it) - rowCount = side - columnCount = side - repeat(side * side) { - val row = it / side - val column = it % side + rowCount = SIDE + columnCount = SIDE + repeat(SIDE * SIDE) { + val row = it / SIDE + val column = it % SIDE val piece = Board.companion_chessTable[it] - val tile = Tile(ctx, (row + column) % 2 == 0, side, + val tile = Tile(ctx, (row + column) % 2 == 0, SIDE, ((if(row==1 || row==0 || row==6 || row==7) getDrawablePiece(piece.pieceType, piece.isWhite) //the if and else is not strictly necessary but its a little optimization else blankIcon)!!), if(row==7) indexToColumn(column) else null, null, it @@ -70,13 +72,13 @@ class BoardView(private val ctx: Context, attrs: AttributeSet?) : GridLayout(ctx } } - fun invalidate(index: Int, piece: Piece ){ + fun invalidate(index: Int, piece: Piece){ if(index<0 || index >= tileMatrix.size) throw IllegalArgumentException() val img = getDrawablePiece(piece.pieceType, piece.isWhite) if(img!=null){ tileMatrix[index]?.setIcon(img)?.invalidate() //log("invalidated") - } else log("invalidate failed") + } else log(TAG,"invalidate failed") } private fun getIcon(xmlID: Int): VectorDrawableCompat? = VectorDrawableCompat.create(ctx.resources, xmlID, null) @@ -95,5 +97,3 @@ class BoardView(private val ctx: Context, attrs: AttributeSet?) : GridLayout(ctx canvas.drawLine(width.toFloat(), 0f, width.toFloat(), height.toFloat(), brush) } } - -fun log(s: String) = Log.i("MY_LOG_BoardView", s) \ No newline at end of file diff --git a/Chess4Android/app/src/main/res/layout-land/activity_game_history.xml b/Chess4Android/app/src/main/res/layout-land/activity_game_history.xml new file mode 100644 index 0000000..cad861f --- /dev/null +++ b/Chess4Android/app/src/main/res/layout-land/activity_game_history.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/Chess4Android/app/src/main/res/layout-land/activity_main.xml b/Chess4Android/app/src/main/res/layout-land/activity_main.xml index bd0275b..26ec548 100644 --- a/Chess4Android/app/src/main/res/layout-land/activity_main.xml +++ b/Chess4Android/app/src/main/res/layout-land/activity_main.xml @@ -11,9 +11,7 @@ android:id="@+id/boardView" android:layout_width="wrap_content" android:layout_height="0dp" - android:layout_marginStart="135dp" - - android:layout_marginEnd="135dp" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/grandTopTitle"> @@ -36,11 +34,12 @@