Note: This is a pre-release of Bagawork. Many things will likely change before the first stable release.

Chess

This is an implementation of chess. Most functionality is implemented, so you can play it to some extent, but the following functionality is still missing:

  • Pawns can't move two steps on the first move
  • Pawns can't capture en passant
  • Pawns can't be turned into other pieces when reaching the opposite side of the board
  • The game never ends/never checks for a winner
  • The game never cheks for checks
Open in Online Editor
class MyApp extends App{
	
	pieces = [
		
		// Top row.
		{c: 0, r: 0, text: ``, team: `black`, type: `rook`},
		{c: 1, r: 0, text: ``, team: `black`, type: `knight`},
		{c: 2, r: 0, text: ``, team: `black`, type: `bishop`},
		{c: 3, r: 0, text: ``, team: `black`, type: `queen`},
		{c: 4, r: 0, text: ``, team: `black`, type: `king`},
		{c: 5, r: 0, text: ``, team: `black`, type: `bishop`},
		{c: 6, r: 0, text: ``, team: `black`, type: `knight`},
		{c: 7, r: 0, text: ``, team: `black`, type: `rook`},
		
		// Second row from the top.
		{c: 0, r: 1, text: ``, team: `black`, type: `pawn`, direction: 1},
		{c: 1, r: 1, text: ``, team: `black`, type: `pawn`, direction: 1},
		{c: 2, r: 1, text: ``, team: `black`, type: `pawn`, direction: 1},
		{c: 3, r: 1, text: ``, team: `black`, type: `pawn`, direction: 1},
		{c: 4, r: 1, text: ``, team: `black`, type: `pawn`, direction: 1},
		{c: 5, r: 1, text: ``, team: `black`, type: `pawn`, direction: 1},
		{c: 6, r: 1, text: ``, team: `black`, type: `pawn`, direction: 1},
		{c: 7, r: 1, text: ``, team: `black`, type: `pawn`, direction: 1},
		
		// Second row from the bottom.
		{c: 0, r: 6, text: ``, team: `white`, type: `pawn`, direction: -1},
		{c: 1, r: 6, text: ``, team: `white`, type: `pawn`, direction: -1},
		{c: 2, r: 6, text: ``, team: `white`, type: `pawn`, direction: -1},
		{c: 3, r: 6, text: ``, team: `white`, type: `pawn`, direction: -1},
		{c: 4, r: 6, text: ``, team: `white`, type: `pawn`, direction: -1},
		{c: 5, r: 6, text: ``, team: `white`, type: `pawn`, direction: -1},
		{c: 6, r: 6, text: ``, team: `white`, type: `pawn`, direction: -1},
		{c: 7, r: 6, text: ``, team: `white`, type: `pawn`, direction: -1},
		
		// Bottom row.
		{c: 0, r: 7, text: ``, team: `white`, type: `rook`},
		{c: 1, r: 7, text: ``, team: `white`, type: `knight`},
		{c: 2, r: 7, text: ``, team: `white`, type: `bishop`},
		{c: 3, r: 7, text: ``, team: `white`, type: `queen`},
		{c: 4, r: 7, text: ``, team: `white`, type: `king`},
		{c: 5, r: 7, text: ``, team: `white`, type: `bishop`},
		{c: 6, r: 7, text: ``, team: `white`, type: `knight`},
		{c: 7, r: 7, text: ``, team: `white`, type: `rook`},
		
	]
	
	nextTeamToMove = `white`
	
	selectedPieceR = -1
	selectedPieceC = -1
	
	createStartPage(){
		return StartPage
	}
	
	selectPiece(r, c){
		a.selectedPieceR = r
		a.selectedPieceC = c
	}
	
	deselectPiece(){
		a.selectedPieceR = -1
		a.selectedPieceC = -1
	}
	
	getSelectedPiece(){
		return a.getPieceByCoordinates(
			a.selectedPieceR,
			a.selectedPieceC
		)
	}
	
	getPieceByCoordinates(r, c){
		return a.pieces.find(p => p.r == r && p.c == c)
	}
	
	moveSelectedPieceTo(r, c){
		
		const arrivingPiece = a.getPieceByCoordinates(r, c)
		
		if(arrivingPiece){
			a.pieces = a.pieces.filter(
				p => p != arrivingPiece
			)
		}
		
		const pieceToMove = a.getSelectedPiece()
		pieceToMove.r = r
		pieceToMove.c = c
		
		a.nextTeamToMove = (a.nextTeamToMove == `white` ? `black` : `white`)
		a.selectedPieceR = -1
		a.selectedPieceC = -1
		
	}
	
	getMovableCoordinates(piece){
		
		if(!piece){
			return []
		}
		
		if(piece.type == `pawn`){
			return a.getPawnMovableCoordinates(piece)
		}
		
		if(piece.type == `king`){
			return a.getKingMovableCoordinates(piece)
		}
		
		if(piece.type == `rook`){
			return a.getRookMovableCoordinates(piece)
		}
		
		if(piece.type == `bishop`){
			return a.getBishopMovableCoordinates(piece)
		}
		
		if(piece.type == `queen`){
			return a.getQueenMovableCoordinates(piece)
		}
		
		if(piece.type == `knight`){
			return a.getKnightMovableCoordinates(piece)
		}
		
		return []
		
	}
	
	getPawnMovableCoordinates(pawnPiece){
		
		const moveableCoordinates = []
		
		// Check one step forward towards the left side of the board.
		if(pawnPiece.c != 0){
			
			const arrivingR = pawnPiece.r + pawnPiece.direction
			const arrivingC = pawnPiece.c - 1
			
			const arrivingPiece = a.getPieceByCoordinates(arrivingR, arrivingC)
			
			if(arrivingPiece && arrivingPiece.team != pawnPiece.team){
				moveableCoordinates.push({
					r: arrivingR,
					c: arrivingC,
				})
			}
			
		}
		
		// Check one step forward.
		const arrivingR = pawnPiece.r + pawnPiece.direction
		const arrivingC = pawnPiece.c
		
		const arrivingPiece = a.getPieceByCoordinates(arrivingR, arrivingC)
		
		if(!arrivingPiece){
			moveableCoordinates.push({
				r: arrivingR,
				c: arrivingC,
			})
		}
		
		// Check one step forward towards the right side of the board.
		if(pawnPiece.c != 7){
			
			const arrivingR = pawnPiece.r + pawnPiece.direction
			const arrivingC = pawnPiece.c + 1
			
			const arrivingPiece = a.getPieceByCoordinates(arrivingR, arrivingC)
			
			if(arrivingPiece && arrivingPiece.team != pawnPiece.team){
				moveableCoordinates.push({
					r: arrivingR,
					c: arrivingC,
				})
			}
			
		}
		
		return moveableCoordinates
		
	}
	
	getKingMovableCoordinates(kingPiece){
		
		const moveableCoordinates = []
		
		function checkSquare(dr, dc){
			
			const arrivingR = kingPiece.r + dr
			const arrivingC = kingPiece.c + dc
			
			if(0 <= arrivingR && arrivingR <= 7){
				if(0 <= arrivingC && arrivingC <= 7){
					
					const arrivingPiece = a.getPieceByCoordinates(arrivingR, arrivingC)
					
					if(!arrivingPiece || arrivingPiece.team != kingPiece.team){
						moveableCoordinates.push({
							r: arrivingR,
							c: arrivingC,
						})
					}
					
				}
			}
			
		}
		
		checkSquare(-1, -1) 
		checkSquare(-1, 0)
		checkSquare(-1, 1)
		checkSquare(0, -1)
		checkSquare(0, 1)
		checkSquare(1, -1)
		checkSquare(1, 0)
		checkSquare(1, 1)
		
		return moveableCoordinates
		
	}
	
	getRookMovableCoordinates(rookPiece){
		
		const moveableCoordinates = []
		
		function checkDirection(dr, dc){
			
			let arrivingR = rookPiece.r
			let arrivingC = rookPiece.c
			
			while(
				(0 <= arrivingR && arrivingR <= 7) &&
				(0 <= arrivingC && arrivingC <= 7)
			){
				
				arrivingR += dr
				arrivingC += dc
				
				const arrivingPiece = a.getPieceByCoordinates(
					arrivingR,
					arrivingC,
				)
				
				if(!arrivingPiece || arrivingPiece.team != rookPiece.team){
					
					moveableCoordinates.push({
						r: arrivingR,
						c: arrivingC,
					})
					
				}
				
				// As soon as we encounter a piece, we can't jump over it,
				// so stop the loop by returning from the function here.
				if(arrivingPiece){
					return
				}
				
			}
			
		}
		
		checkDirection(0, 1)
		checkDirection(0, -1)
		checkDirection(1, 0)
		checkDirection(-1, 0)
		
		return moveableCoordinates
		
	}
	
	getBishopMovableCoordinates(bishopPiece){
		
		const moveableCoordinates = []
		
		function checkDirection(dr, dc){
			
			let arrivingR = bishopPiece.r
			let arrivingC = bishopPiece.c
			
			while(
				(0 <= arrivingR && arrivingR <= 7) &&
				(0 <= arrivingC && arrivingC <= 7)
			){
				
				arrivingR += dr
				arrivingC += dc
				
				const arrivingPiece = a.getPieceByCoordinates(
					arrivingR,
					arrivingC,
				)
				
				if(!arrivingPiece || arrivingPiece.team != bishopPiece.team){
					
					moveableCoordinates.push({
						r: arrivingR,
						c: arrivingC,
					})
					
				}
				
				// As soon as we encounter a piece, we can't jump over it,
				// so stop the loop by returning from the function here.
				if(arrivingPiece){
					return
				}
				
			}
			
		}
		
		checkDirection(1, 1)
		checkDirection(1, -1)
		checkDirection(-1, 1)
		checkDirection(-1, -1)
		
		return moveableCoordinates
		
	}
	
	getQueenMovableCoordinates(queenPiece){
		
		return a.getRookMovableCoordinates(queenPiece).concat(
			a.getBishopMovableCoordinates(queenPiece),
		)
		
	}
	
	getKnightMovableCoordinates(knightPiece){
		
		const moveableCoordinates = []
		
		function checkSquare(dr, dc){
			
			const arrivingR = knightPiece.r + dr
			const arrivingC = knightPiece.c + dc
			
			if(
				(0 <= arrivingR && arrivingR <= 7) &&
				(0 <= arrivingC && arrivingC <= 7)
			){
				
				const arrivingPiece = a.getPieceByCoordinates(
					arrivingR,
					arrivingC,
				)
				
				if(!arrivingPiece || arrivingPiece.team != knightPiece.team){
					
					moveableCoordinates.push({
						r: arrivingR,
						c: arrivingC,
					})
					
				}
				
			}
			
		}
		
		checkSquare(-2, -1)
		checkSquare(-2,  1)
		checkSquare(-1, -2)
		checkSquare(-1,  2)
		checkSquare(1 , -2)
		checkSquare(1 ,  2)
		checkSquare(2 , -1)
		checkSquare(2 ,  1)
		
		return moveableCoordinates
		
	}
	
}
class StartPage extends Page{
	
	createCellComponents(){
		
		const selectedPiece = a.getSelectedPiece()
		const possibleMoveToCoordinates = a.getMovableCoordinates(selectedPiece)
		
		const rows = []
		
		for(let r=0; r<8; r++){
			
			const columns = [
				Text.text(`${8-r}`).size(1).border(0.5, `black`)
			]
			
			for(let c=0; c<8; c++){
				
				const backgroundColor = (
					r % 2 == c % 2 ?
					`yellow` :
					`silver`
				)
				
				const piece = a.getPieceByCoordinates(r, c)
				const canSelectedMoveToCurrentSquare = (
					selectedPiece && possibleMoveToCoordinates.some(
						coord => coord.r == r && coord.c == c,
					)
				)
				
				let square = null
				
				if(selectedPiece && selectedPiece == piece){
					square = Button.text(piece.text).handler(a.deselectPiece)
				}else if(canSelectedMoveToCurrentSquare){
					square = Button.text(piece?.text ?? ` `).handler(a.moveSelectedPieceTo, r, c)
				}else if(!piece){
					square = Text.text(` `)
				}else if(!selectedPiece && piece.team == a.nextTeamToMove){
					square = Button.text(piece?.text ?? ` `).handler(a.selectPiece, r, c)
				}else{
					square = Text.text(piece.text)
				}
				
				columns.push(
					Box
						.size(1)
						.border(0.5, `black`)
						.backgroundColor(backgroundColor)
						.aspectRatio(1, 1)
						.child(square),
				)
				
			}
			
			rows.push(
				Columns.size(1).children(
					...columns
				)
			)
			
		}
		
		return Rows.border(0.5, `black`).children(
			rows,
			Columns.size(1).children([
				Space.size(1).border(0.5, `black`),
				Text.text(`A`).size(1).border(0.5, `black`),
				Text.text(`B`).size(1).border(0.5, `black`),
				Text.text(`C`).size(1).border(0.5, `black`),
				Text.text(`D`).size(1).border(0.5, `black`),
				Text.text(`E`).size(1).border(0.5, `black`),
				Text.text(`F`).size(1).border(0.5, `black`),
				Text.text(`G`).size(1).border(0.5, `black`),
				Text.text(`H`).size(1).border(0.5, `black`),
			])
		)
		
	}
	
	createGui(){
		return Rows.padding(1).children(
			Text.text(`Chess`),
			Box.size(1).aspectRatio(1, 1).child(
				p.createCellComponents(),
			),
			Text.text(`Good luck!`),
		)
	}
	
}