diff --git a/Chess4Android/.gradle/7.0.2/executionHistory/executionHistory.bin b/Chess4Android/.gradle/7.0.2/executionHistory/executionHistory.bin index 1d5d105..b85ccd2 100644 Binary files a/Chess4Android/.gradle/7.0.2/executionHistory/executionHistory.bin and b/Chess4Android/.gradle/7.0.2/executionHistory/executionHistory.bin differ diff --git a/Chess4Android/.gradle/7.0.2/executionHistory/executionHistory.lock b/Chess4Android/.gradle/7.0.2/executionHistory/executionHistory.lock index 862e78a..69cb503 100644 Binary files a/Chess4Android/.gradle/7.0.2/executionHistory/executionHistory.lock and b/Chess4Android/.gradle/7.0.2/executionHistory/executionHistory.lock differ diff --git a/Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.bin b/Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.bin index 7b23840..deb307b 100644 Binary files a/Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.bin and b/Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.bin differ diff --git a/Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.lock b/Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.lock index 40ef56b..9d41c68 100644 Binary files a/Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.lock and b/Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.lock differ diff --git a/Chess4Android/.gradle/7.0.2/fileHashes/resourceHashesCache.bin b/Chess4Android/.gradle/7.0.2/fileHashes/resourceHashesCache.bin index 7d830e0..4c0a8a8 100644 Binary files a/Chess4Android/.gradle/7.0.2/fileHashes/resourceHashesCache.bin and b/Chess4Android/.gradle/7.0.2/fileHashes/resourceHashesCache.bin differ diff --git a/Chess4Android/.gradle/7.0.2/javaCompile/classAnalysis.bin b/Chess4Android/.gradle/7.0.2/javaCompile/classAnalysis.bin index a5ed339..542efa7 100644 Binary files a/Chess4Android/.gradle/7.0.2/javaCompile/classAnalysis.bin and b/Chess4Android/.gradle/7.0.2/javaCompile/classAnalysis.bin differ diff --git a/Chess4Android/.gradle/7.0.2/javaCompile/jarAnalysis.bin b/Chess4Android/.gradle/7.0.2/javaCompile/jarAnalysis.bin index 03ad5db..24f20fb 100644 Binary files a/Chess4Android/.gradle/7.0.2/javaCompile/jarAnalysis.bin and b/Chess4Android/.gradle/7.0.2/javaCompile/jarAnalysis.bin differ diff --git a/Chess4Android/.gradle/7.0.2/javaCompile/javaCompile.lock b/Chess4Android/.gradle/7.0.2/javaCompile/javaCompile.lock index 1d484c8..d51e39b 100644 Binary files a/Chess4Android/.gradle/7.0.2/javaCompile/javaCompile.lock and b/Chess4Android/.gradle/7.0.2/javaCompile/javaCompile.lock differ diff --git a/Chess4Android/.gradle/7.0.2/javaCompile/taskHistory.bin b/Chess4Android/.gradle/7.0.2/javaCompile/taskHistory.bin index ca10e73..8f00ce9 100644 Binary files a/Chess4Android/.gradle/7.0.2/javaCompile/taskHistory.bin and b/Chess4Android/.gradle/7.0.2/javaCompile/taskHistory.bin differ diff --git a/Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index d204c0f..074bf44 100644 Binary files a/Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/AboutActivity.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/AboutActivity.kt index b883d2d..68a2661 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/AboutActivity.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/AboutActivity.kt @@ -20,5 +20,12 @@ class AboutActivity : AppCompatActivity() { } startActivity(intent) } + + findViewById(R.id.lichessTextView).setOnClickListener{ + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(LICHESSDAILYPUZZLEURL)).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + startActivity(intent) + } } } \ No newline at end of file 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 5f24ce1..c28db28 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 @@ -29,14 +29,12 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle private const val TAG = "MY_LOG_MainActivity" -private const val LICHESSDAILYPUZZLEURL: String = "https://lichess.org/api/puzzle/daily" +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 PUZZLE = "puzzle" const val SOLUTION = "solution" -const val ISWHITES = "white" -//Bundle data keys -const val SCREEN_ORIENTATION = "screen" + class MainActivity : AppCompatActivity() { private var getGameButton: Button? = null @@ -87,10 +85,9 @@ class MainActivity : AppCompatActivity() { toast(R.string.puzzleUpdated) log(thisViewModel.lichessGameOfTheDayPuzzle) log(thisViewModel.lichessGameOfTheDaySolution) - log(thisViewModel.lichessisWhitesOnTop.toString()) thisViewModel.updateDisplayed.value=true } - } else snackBar(R.string.connectionError, MainActivityViewModel::getTodaysGame) + } else snackBar(R.string.connectionError) } continueButton?.setOnClickListener { launchGame() } @@ -124,10 +121,10 @@ class MainActivity : AppCompatActivity() { } } - private fun snackBar(stringID: Int, kFunction1: (MainActivityViewModel) -> Unit){ //https://material.io/components/snackbars/android#using-snackbars //or function: () -> (Unit) https://stackoverflow.com/a/44132689 + private fun snackBar(stringID: Int){ //https://material.io/components/snackbars/android#using-snackbars //or function: () -> (Unit) https://stackoverflow.com/a/44132689 Snackbar.make(findViewById(R.id.getGameButton),getString(stringID), Snackbar.LENGTH_INDEFINITE) .setAction(R.string.retry) { - kFunction1 + thisViewModel.getTodaysGame() } .show() } @@ -141,7 +138,6 @@ class MainActivity : AppCompatActivity() { val intent = Intent(this, PuzzleSolvingActivity::class.java).apply { putExtra(PUZZLE, thisViewModel.lichessGameOfTheDayPuzzle) putExtra(SOLUTION, thisViewModel.lichessGameOfTheDaySolution) - putExtra(ISWHITES, thisViewModel.lichessisWhitesOnTop) } startActivity(intent) } @@ -171,7 +167,6 @@ class MainActivityViewModel(application: Application, private val state: SavedSt //since we didnt absolutely need to notify the Activity when the data changed, using LiveData isnt necessarily necessary var lichessGameOfTheDayPuzzle: Array? = null var lichessGameOfTheDaySolution: Array? = null - var lichessisWhitesOnTop: Boolean? = null val isGameReady: LiveData = state.getLiveData(IS_GAME_READY_LIVEDATA_KEY) val context = getApplication() var currentScreenOrientation: MutableLiveData = MutableLiveData(context.resources.configuration.orientation) @@ -187,7 +182,6 @@ class MainActivityViewModel(application: Application, private val state: SavedSt lichessGameOfTheDayPuzzle = lichessGameOfTheDay?.game?.pgn?.split(" ")?.toTypedArray() lichessGameOfTheDaySolution = lichessGameOfTheDay?.puzzle?.solution - lichessisWhitesOnTop = lichessGameOfTheDay?.game.players[0].color == "white" if(isDataNullOrEmpty()){ state.set(IS_GAME_READY_LIVEDATA_KEY, false) } else state.set(IS_GAME_READY_LIVEDATA_KEY, true) //when this code executes, the code in "thisActivityViewModel.isGameReady.observe(this)" is also executed @@ -203,7 +197,7 @@ class MainActivityViewModel(application: Application, private val state: SavedSt log("Request finished") } - fun isDataNullOrEmpty() = lichessGameOfTheDayPuzzle==null || lichessGameOfTheDaySolution==null || lichessGameOfTheDayPuzzle?.size==0 || lichessGameOfTheDaySolution?.size==0 || lichessisWhitesOnTop==null + fun isDataNullOrEmpty() = lichessGameOfTheDayPuzzle==null || lichessGameOfTheDaySolution==null || lichessGameOfTheDayPuzzle?.size==0 || lichessGameOfTheDaySolution?.size==0 //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 7c62f97..25a5a20 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 @@ -7,13 +7,13 @@ import android.util.Log import android.widget.Toast import androidx.activity.viewModels import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle -import pt.isel.pdm.chess4android.model.BOARDLENGHT -import pt.isel.pdm.chess4android.model.Board -import pt.isel.pdm.chess4android.model.PIECETYPE +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 import pt.isel.pdm.chess4android.views.tileMatrix private const val TAG = "MY_LOG_PuzzleSolvingActivity" @@ -22,12 +22,15 @@ class PuzzleSolvingActivity : AppCompatActivity() { private var lichessGameOfTheDayPuzzle: Array? = null private var lichessGameOfTheDaySolution: Array? = null - private var lichessIsWhitesOnTop: Boolean = false private lateinit var myView: BoardView private var currentlySelectedPieceIndex: Int = -1 + private val binding by lazy { + ActivityMainBinding.inflate(layoutInflater) + } + private val thisViewModel: PuzzleSolvingAcitivityViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { @@ -39,63 +42,95 @@ class PuzzleSolvingActivity : AppCompatActivity() { lichessGameOfTheDayPuzzle = intent.getStringArrayExtra(PUZZLE) lichessGameOfTheDaySolution = intent.getStringArrayExtra(SOLUTION) - lichessIsWhitesOnTop = intent.getBooleanExtra(ISWHITES, false) myView = findViewById(R.id.boardView) tileMatrix.forEach { tile -> - tile?.setOnClickListener{ - if(currentlySelectedPieceIndex==-1) { - if(thisViewModel.board.getPieceAtIndex(tile.index).pieceType!=PIECETYPE.EMPTY){ - currentlySelectedPieceIndex = tile.index - val pieceColor = thisViewModel.board.getPieceAtIndex(currentlySelectedPieceIndex).isWhite - if(thisViewModel.isWhitesPlaying==pieceColor){ - val piecetype = thisViewModel.board.getPieceAtIndex(currentlySelectedPieceIndex).pieceType - log("picked a $piecetype") - } else if(pieceColor){ - log("hey! the black pieces are playing") - currentlySelectedPieceIndex = -1 - } else { - log("hey! the white pieces are playing") - currentlySelectedPieceIndex = -1 - } - } else { - log("you picked a empty spot...") + tile?.setOnClickListener { + tileBehaviour(tile) + } + } + + if(thisViewModel.isGameLoaded.value==true){ + invalidateEverything() + } else if(loadGame()) invalidateEverything() //it's easier for us to invalidate everything when loading + } + + private fun tileBehaviour(tile: Tile) { + if(currentlySelectedPieceIndex==-1) { + if(thisViewModel.board.getPieceAtIndex(tile.index).pieceType!=PIECETYPE.EMPTY){ + currentlySelectedPieceIndex = tile.index + val pieceColor = thisViewModel.board.getPieceAtIndex(currentlySelectedPieceIndex).isWhite + when { + thisViewModel.isWhitesPlaying==pieceColor -> { + val piecetype = thisViewModel.board.getPieceAtIndex(currentlySelectedPieceIndex).pieceType + log("picked a $piecetype") + } + pieceColor -> { + log("hey! the black pieces are playing") + currentlySelectedPieceIndex = -1 + } + else -> { + log("hey! the white pieces are playing") currentlySelectedPieceIndex = -1 } } - else { - log("analysing movement validity:") - val pieceToMove = thisViewModel.board.getPieceAtIndex(currentlySelectedPieceIndex) - val pieceThatWillBeOverwrittenIndex = tile.index - val theNewPosition = thisViewModel.board.getPieceAtIndex(pieceThatWillBeOverwrittenIndex)?.position - log("destination has index = $pieceThatWillBeOverwrittenIndex and position = $theNewPosition ") - if(theNewPosition!=null && pieceToMove!=null && pieceThatWillBeOverwrittenIndex!=currentlySelectedPieceIndex) { - if(thisViewModel.board.getPieceAtIndex(pieceThatWillBeOverwrittenIndex).pieceType==PIECETYPE.EMPTY || pieceToMove.isWhite!=thisViewModel.board.getPieceAtIndex(pieceThatWillBeOverwrittenIndex).isWhite){ - if(pieceToMove.canMoveTo(theNewPosition)){ - thisViewModel.board.movePieceToAndLeaveEmptyBehind(pieceThatWillBeOverwrittenIndex, pieceToMove) - myView.invalidate(pieceThatWillBeOverwrittenIndex, thisViewModel.board.getPieceAtIndex(pieceThatWillBeOverwrittenIndex)!!) //new pos - myView.invalidate(currentlySelectedPieceIndex, thisViewModel.board.getPieceAtIndex(currentlySelectedPieceIndex)!! ) //old pos - log("moved") - thisViewModel.isWhitesPlaying=!thisViewModel.isWhitesPlaying - } 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") - currentlySelectedPieceIndex = -1 - } + } else { + log("you picked a empty spot...") + currentlySelectedPieceIndex = -1 } } + else { + 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 movement = pieceToMove.position.letterAndNumber()+theNewPosition.letterAndNumber() + if(movement == lichessGameOfTheDaySolution?.get(thisViewModel.movementNumber)) { + thisViewModel.movementNumber++ + toast(R.string.correctMove) + } + log("destination has index = $pieceThatWillBeEatenIndex and position = $theNewPosition ") + if(theNewPosition!=null && pieceToMove!=null && pieceThatWillBeEatenIndex!=currentlySelectedPieceIndex) { + if(thisViewModel.board.getPieceAtIndex(pieceThatWillBeEatenIndex).pieceType==PIECETYPE.EMPTY || pieceToMove.isWhite!=thisViewModel.board.getPieceAtIndex(pieceThatWillBeEatenIndex).isWhite){ + if(pieceToMove.canMoveTo(theNewPosition)){ + if(pieceToMove.pieceType==PIECETYPE.PAWN) { + val thePawn = pieceToMove as ChessPieces.Pawn + if(thePawn.movesDiagonally(theNewPosition) && thePieceToBeEaten.pieceType==PIECETYPE.EMPTY){ + log("the pawn can only move diagonally when it will eat a piece") + } else moveIt(pieceThatWillBeEatenIndex, pieceToMove) + } else { + moveIt(pieceThatWillBeEatenIndex, pieceToMove) + } + } 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") + currentlySelectedPieceIndex = -1 + } + } - if(lichessIsWhitesOnTop) { - thisViewModel.board.reverseBoard() - //invalidateEverything() + private fun moveIt(pieceThatWillBeEatenIndex: Int, pieceToMove: Piece) { + thisViewModel.board.movePieceToAndLeaveEmptyBehind(pieceThatWillBeEatenIndex, pieceToMove) + myView.invalidate(pieceThatWillBeEatenIndex, thisViewModel.board.getPieceAtIndex(pieceThatWillBeEatenIndex)!!) //new pos + myView.invalidate(currentlySelectedPieceIndex, thisViewModel.board.getPieceAtIndex(currentlySelectedPieceIndex)!! ) //old pos + log("moved") + thisViewModel.isWhitesPlaying=!thisViewModel.isWhitesPlaying + if(thisViewModel.movementNumber==lichessGameOfTheDaySolution?.size) { + snackBar(R.string.won) } + } - if(thisViewModel.isGameLoaded.value==true){ - invalidateEverything() - } else if(loadGame()) invalidateEverything() //it's easier for us to invalidate everything when loading + private fun snackBar(stringID: Int){ //https://material.io/components/snackbars/android#using-snackbars //or function: () -> (Unit) https://stackoverflow.com/a/44132689 + Snackbar.make(findViewById(R.id.boardView),getString(stringID), Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.ok) { + super.onBackPressed() + } + .show() } + + override fun onStart() { log("Started") super.onStart() @@ -149,7 +184,6 @@ class PuzzleSolvingActivity : AppCompatActivity() { private fun toast(id: Int) = toast(getString(id)) - private fun log(s: String) = Log.i(TAG, s) } private fun log(s: String) = Log.i(TAG, s) //since both of the classes on this file use log, I put it here @@ -160,5 +194,6 @@ class PuzzleSolvingAcitivityViewModel(application: Application, private val stat } var isGameLoaded: MutableLiveData = MutableLiveData(false) var board: Board = Board() + var movementNumber: Int = 0 var isWhitesPlaying: Boolean = true } \ No newline at end of file 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 9b12c5c..e776f52 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 @@ -301,26 +301,35 @@ class Board { private fun getPieceThatCanMoveTo(position: Position, array: IntArray) : Piece? { var piece: Piece? + /* + * due to the fact that in the game of 21/11/2021 (id=KU4e1TVf) + * The leftmost horse in c3 and the rightmost horse in g1 could both go to e2, but in the + * json, the indication of the column was missing, I decided to choose the rightmost + * piece (which is what happens during the process of the movements in https://lichess.org/training/daily) + * this was due to the fact that errors would occur, per example, in the kings move, because the + * knight that was intended to move didnt move, and thus, a kings move would fail + */ + var secondaryPiece: Piece? = null for(i in array){ if(i==-1) break piece = getPieceAtIndex(i) - if(piece.canMoveTo(position)) return piece //if the piece can perform the move, return it + //if the piece can perform the move, return it + if(piece.canMoveTo(position)) { + if(secondaryPiece==null){ + secondaryPiece=piece + } else { + if(secondaryPiece.position.letter < piece.position.letter) return piece + return secondaryPiece + } + } } - return null + return secondaryPiece } fun reverseBoard(){ //only the visual representation is reversed chessPiecesTablePositions.reversedArray() } - /*fun switchPiecesColor() { - chessPiecesTablePositions.forEachIndexed { index, piece -> - do { - switchPiecesAtIndexes(index, positionToIndex(piece.position.horizontalyInvertPosition())) - } while(piece.pieceType!=PIECETYPE.EMPTY) - } - }*/ - fun reset() { repeat(BOARDLENGHT) { setPieceAtIndex(it, startingChessPiecesTablePositions[it] ) 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 ade88e2..25880fc 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 @@ -30,11 +30,13 @@ data class Position(var letter: Char, var number: Byte) { } 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 //these 2 methods bellow are useful for pieces that have specific and strict movement patterns, like pawns, rooks and bishops. Per example, kings and queens dont need these methods because they can move freely to any direction, but are only restricted by the movement distance. Thus, for these 2 examples, only isValidMovement is used. fun getXDiference(destination: Position) : Int = abs(this.letter-destination.letter) //int to make it (down casting to byte) easy 4 us fun getYDiference(destination: Position) : Int = abs(this.number-destination.number) //int to make it (down casting to byte) easy 4 us + fun getYDiferenceNoAbs(destination: Position) : Int = (this.number-destination.number) fun isValidMovement(destination: Position, maxX: Byte, maxY: Byte) : Boolean { if(maxX<0 || maxY<0 || maxX >= BOARD_SIDE_SIZE || maxY >= BOARD_SIDE_SIZE) throw IllegalArgumentException() @@ -80,15 +82,24 @@ 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 && position.getYDiference(destination)<=2) { //part that checks piece movement *logic*, and its this format for every moveTo method + 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 return true - } else if(position.getYDiference(destination) == 1){ + } else if (!firstMoveUsed && !isWhite && (position.getYDiferenceNoAbs(destination) in 1..2)){ + firstMoveUsed = true + return true + } else if(isWhite && position.getYDiferenceNoAbs(destination) == -1){ + return true + } else if(!isWhite && position.getYDiferenceNoAbs(destination) == 1){ //the !isWhite && is actually necessary! return true } } return false } + fun movesDiagonally(destination: Position) : Boolean = position.getXDiference(destination)==1 + + override fun toString(): String = "Pawn" } diff --git a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/LichessJSON.kt b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/LichessJSON.kt index 0481160..35465f8 100644 --- a/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/LichessJSON.kt +++ b/Chess4Android/app/src/main/java/pt/isel/pdm/chess4android/model/LichessJSON.kt @@ -37,9 +37,4 @@ data class Player ( data class Perf( val icon: String, val name: String -) - - - - - +) \ No newline at end of file diff --git a/Chess4Android/app/src/main/res/layout-land/activity_about.xml b/Chess4Android/app/src/main/res/layout-land/activity_about.xml index 2322ffa..4a1024a 100644 --- a/Chess4Android/app/src/main/res/layout-land/activity_about.xml +++ b/Chess4Android/app/src/main/res/layout-land/activity_about.xml @@ -40,4 +40,14 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/gitHubButtonAndImage" app:srcCompat="@mipmap/ic_github_icon" /> + + \ No newline at end of file diff --git a/Chess4Android/app/src/main/res/layout/activity_about.xml b/Chess4Android/app/src/main/res/layout/activity_about.xml index 2322ffa..4a1024a 100644 --- a/Chess4Android/app/src/main/res/layout/activity_about.xml +++ b/Chess4Android/app/src/main/res/layout/activity_about.xml @@ -40,4 +40,14 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/gitHubButtonAndImage" app:srcCompat="@mipmap/ic_github_icon" /> + + \ No newline at end of file diff --git a/Chess4Android/app/src/main/res/values-pt-rPT/strings.xml b/Chess4Android/app/src/main/res/values-pt-rPT/strings.xml index 9f997ee..1155255 100644 --- a/Chess4Android/app/src/main/res/values-pt-rPT/strings.xml +++ b/Chess4Android/app/src/main/res/values-pt-rPT/strings.xml @@ -17,4 +17,8 @@ Tente acabar o jogo Bem vindo Não te preocupes, eu sobrevivi à rotação do ecrã! + A API da lichess para o puzzle diário + Movimento correto! + Você ganhou! + Ok \ No newline at end of file diff --git a/Chess4Android/app/src/main/res/values/strings.xml b/Chess4Android/app/src/main/res/values/strings.xml index eb3ded8..b3521c5 100644 --- a/Chess4Android/app/src/main/res/values/strings.xml +++ b/Chess4Android/app/src/main/res/values/strings.xml @@ -16,4 +16,8 @@ Welcome Try to finish the game Don\'t worry, I survived the screen rotation! + The lichess daily puzzle API + Correct move! + You won! + Ok \ No newline at end of file diff --git a/Notes.md b/Notes.md new file mode 100644 index 0000000..343d429 --- /dev/null +++ b/Notes.md @@ -0,0 +1,31 @@ +## About the encoding of the json +- The movements to build up the puzzle are in the property: **game.pgn** +- The solution to the puzzle is in the property: **puzzle.solution** + +- No letter -> Pawn +- B -> Bishop +- N -> Knight +- R -> Rook +- K -> King +- Q -> Queen +- ?x?? -> Movement that envolves a piece from the column ? killing the piece in position ??. b**x**c5 per example + +## Special movements for the king (only when the king is selected, this is possible, when selecting the rook, it can't switch with king) +- O-O -> Kingside castle +- O-O-O -> Queenside castle (meaning the move is done towards the side of where the queen is initially positioned at, in respective to the piece's color) +- [Read more here](https://en.wikipedia.org/wiki/Castling +- [How to "castle"](https://youtu.be/4jXQyGaeUV8) + +### Extra (these symbols are placed at the end of a move): +- \+ -> means, that it's a check (the color that played can capture the opposite color king) +- \# -> means check mate (game over) + +### Extra rules +- On a check, you can only move pieces that kill the piece that made the check, block it's path, or move the king out of the reach of that piece + +#### When a pawn reaches the opponent's side limit, it can turn into: a queen, horse, tower or bishop. Format examples: +- d1=q //turns into queen +- d1=q# // turns into queen and checks king +- gxh1=g // eats piece and turns into queen +- gxh8=q# // pawn at g7 ate rook at h8, turns into queen and checks king + diff --git a/README.md b/README.md index ca95437..582b8bf 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,35 @@ Using: lichess API - Tiago Pilaro 46147 ### Tag **chess_royale_1** -This app gets today's chess puzzle and allows the user to solve it. +This app gets today's chess puzzle and displays it to the screen. The user can move pieces around according to the most fundamental chess rules. The confirmation that the moves inputed by the user go along with the solution is performed, but it wasn't extensively tested yet. It worked for the puzzle in the example bellow in the picture of the activity 2. The player will have to play with both piece colors -#### Activity 1 +The limitations in this tag are the organization of the code and the lack of 1 chess rule (to my knowledge): +- **The carryover of the translation of the list of the moves indicated by the json to correctly alter or reflect upon the pieces's condition-states is not totally yet considered as do some other chess rules**. Per example, when a pawn reaches the other side, it turns into another piece except king and pawn, or handling of the king and queens castles when done by the user. This is some way is because the pieces dont yet have the capacity or access to have knowledge of the piece it's going to eat or affect, it only knows the position that the controller told him to go. This involves giving more info to the pieces. This fauly design also made the listener we set up for the tile a bit big because only the controller has acess to the board, and each individual piece doesn't. +- General organization and uniformization of the coding of the moves -#### Activity 2 +### Activity 1 +![](_imgs/T1_1.png) -#### Activity 3 +### Activity 2 +Example from puzzle with id = KU4e1TVf + +Date: 21/11/2021 + +![](_imgs/T1_2.png) + +### Activity 2 after completing a game +![](_imgs/T1_2_2.png) + +### Activity 3 +![](_imgs/T1_3.png) + +### Navigation details +- For every activity, it supports rotating the phone to landscape. No data will be lost due to the use of ViewModel +- In the 1st Activity, there's a button called **get todays game" to get the puzzle and solution from the API and loads it into memory. The continue button will be enabled once this is done. If the request fails, it will launch an appropriate snackBar for the matter. Clicking back on the main Activity will not destroy it since onBackPressed was redefined. +- In the 2nd Activity, a toast will appear if the movement corresponds to the solution. And a snackBar will appear if the user completed the puzzle, on click, it will return to the main activity +- In the 3rd Activity, there's a textView with the names of the authors, a textView that acts like a button which links to this repo, an image of the github icon, and a textView that also acts like a button which will open the URL to the daily puzzle API. + +### Tag **chess_royale_2** ## Consult today's game here - https://lichess.org/training/daily @@ -31,32 +53,4 @@ This app gets today's chess puzzle and allows the user to solve it. - https://levelup.gitconnected.com/finding-all-legal-chess-moves-2cb872d05bc6 - http://www.jimmyvermeer.com/rules.html - https://www.chess.com/forum/view/game-analysis/in-a-computer-analysis---means-what -- https://www.chess.com/forum/view/general/all-the-terminology-and-symbol-meanings - -- No letter -> Pawn -- B -> Bishop -- N -> Knight -- R -> Rook -- K -> King -- Q -> Queen -- ?x?? -> Movement that envolves a piece from the column ? killing the piece in position ??. b**x**c5 per example - -## Special movements for the king (only when the king is selected, this is possible, when selecting the rook, it can't switch with king) -- O-O -> Kingside castle -- O-O-O -> Queenside castle (meaning the move is done towards the side of where the queen is initially positioned at, in respective to the piece's color) -- [Read more here](https://en.wikipedia.org/wiki/Castling -- [How to "castle"](https://youtu.be/4jXQyGaeUV8) - -### Extra (these symbols are placed at the end of a move): -- \+ -> means, that it's a check (the color that played can capture the opposite color king) -- \# -> means check mate (game over) - -### Extra rules -- On a check, you can only move pieces that kill the piece that made the check, block it's path, or move the king out of the reach of that piece - -#### When a pawn reaches the opponent's side limit, it can turn into: a queen, horse, tower or bishop. Format: -- d1=q //turns into queen -- d1=q# // turns into queen and checks king -- gxh1=g // eats piece and turns into queen -- gxh8=q# // pawn at g7 ate rook at h8, turns into queen and checks king - +- https://www.chess.com/forum/view/general/all-the-terminology-and-symbol-meanings \ No newline at end of file diff --git a/_imgs/T1_1.png b/_imgs/T1_1.png new file mode 100644 index 0000000..5be73dd Binary files /dev/null and b/_imgs/T1_1.png differ diff --git a/_imgs/T1_2.png b/_imgs/T1_2.png new file mode 100644 index 0000000..34e9394 Binary files /dev/null and b/_imgs/T1_2.png differ diff --git a/_imgs/T1_2_2.png b/_imgs/T1_2_2.png new file mode 100644 index 0000000..9c5d0b2 Binary files /dev/null and b/_imgs/T1_2_2.png differ diff --git a/_imgs/T1_3.png b/_imgs/T1_3.png new file mode 100644 index 0000000..ef17e9e Binary files /dev/null and b/_imgs/T1_3.png differ