Skip to content

Commit

Permalink
Tag1 commit (read description)
Browse files Browse the repository at this point in the history
- Fixed the snackBar in MainActivity once and for all (in the previous commit I didnt test it)
- Correctly configured the rules for the pawn (can only move sideways when eating and can only move 2 places for his 1st time moving)
- Implemented a check if the user is going along with the puzzle solution, on completion, it shows a snackBar, which, on click returns to main activity
- Added a condition of when 2 pieces can go to the same position and if the column isnt specified by the json, I choose the rightmost piece. IDK if that's the rule of thumb, but the rule I interpreted and went along after checking on the lichess website the page that shows the game for today (21/11/2021, id=KU4e1TVf). And in that conflicted move, the rightmost position (the knight) was selected. And on that page, the column WAS specified for that move, but not in the json. So, there's that.

Could simplify more stuff like checking conditions in if's, as always, but that's life
  • Loading branch information
p4ulor committed Nov 21, 2021
1 parent 99ca32a commit 09ff5b0
Show file tree
Hide file tree
Showing 26 changed files with 216 additions and 112 deletions.
Binary file not shown.
Binary file not shown.
Binary file modified Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.bin
Binary file not shown.
Binary file modified Chess4Android/.gradle/7.0.2/fileHashes/fileHashes.lock
Binary file not shown.
Binary file modified Chess4Android/.gradle/7.0.2/fileHashes/resourceHashesCache.bin
Binary file not shown.
Binary file modified Chess4Android/.gradle/7.0.2/javaCompile/classAnalysis.bin
Binary file not shown.
Binary file modified Chess4Android/.gradle/7.0.2/javaCompile/jarAnalysis.bin
Binary file not shown.
Binary file modified Chess4Android/.gradle/7.0.2/javaCompile/javaCompile.lock
Binary file not shown.
Binary file modified Chess4Android/.gradle/7.0.2/javaCompile/taskHistory.bin
Binary file not shown.
Binary file modified Chess4Android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,12 @@ class AboutActivity : AppCompatActivity() {
}
startActivity(intent)
}

findViewById<TextView>(R.id.lichessTextView).setOnClickListener{
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(LICHESSDAILYPUZZLEURL)).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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() }
Expand Down Expand Up @@ -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()
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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<String>? = null
var lichessGameOfTheDaySolution: Array<String>? = null
var lichessisWhitesOnTop: Boolean? = null
val isGameReady: LiveData<Boolean> = state.getLiveData(IS_GAME_READY_LIVEDATA_KEY)
val context = getApplication<Application>()
var currentScreenOrientation: MutableLiveData<Int> = MutableLiveData(context.resources.configuration.orientation)
Expand All @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -22,12 +22,15 @@ class PuzzleSolvingActivity : AppCompatActivity() {

private var lichessGameOfTheDayPuzzle: Array<String>? = null
private var lichessGameOfTheDaySolution: Array<String>? = 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?) {
Expand All @@ -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()
Expand Down Expand Up @@ -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
Expand All @@ -160,5 +194,6 @@ class PuzzleSolvingAcitivityViewModel(application: Application, private val stat
}
var isGameLoaded: MutableLiveData<Boolean> = MutableLiveData(false)
var board: Board = Board()
var movementNumber: Int = 0
var isWhitesPlaying: Boolean = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -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] )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,4 @@ data class Player (
data class Perf(
val icon: String,
val name: String
)





)
Loading

0 comments on commit 09ff5b0

Please sign in to comment.