mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2024-12-19 20:37:51 +00:00
enhanced
This commit is contained in:
parent
813b74e94c
commit
ba771d54af
5
apps_zoo/chess_game/description.yaml
Normal file
5
apps_zoo/chess_game/description.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
description: This is a 2 players chess game
|
||||
version: 1.0
|
||||
author: ParisNeo
|
||||
model: gpt-4o-mini
|
||||
disclaimer: None
|
BIN
apps_zoo/chess_game/icon.png
Normal file
BIN
apps_zoo/chess_game/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
639
apps_zoo/chess_game/index.html
Normal file
639
apps_zoo/chess_game/index.html
Normal file
@ -0,0 +1,639 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Chess Game with Check and Checkmate</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
.game-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
#chessboard {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 60px);
|
||||
grid-template-rows: repeat(8, 60px);
|
||||
border: 2px solid #333;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
.square {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 40px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
.white {
|
||||
background-color: #f0d9b5;
|
||||
}
|
||||
.black {
|
||||
background-color: #b58863;
|
||||
}
|
||||
.selected {
|
||||
background-color: #7fc97f;
|
||||
}
|
||||
.possible-move {
|
||||
position: relative;
|
||||
}
|
||||
.possible-move::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.in-check {
|
||||
background-color: #ff6b6b;
|
||||
}
|
||||
.turn-indicator {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.game-status {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 20px;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #ff6b6b;
|
||||
}
|
||||
.scoreboard {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.scoreboard h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.score {
|
||||
font-size: 18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaderboard {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.leaderboard ol {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.winner-input {
|
||||
display: none;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.credits {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.credits h1 {
|
||||
margin: 0;
|
||||
font-size: 48px;
|
||||
}
|
||||
.credits p {
|
||||
font-size: 24px;
|
||||
}
|
||||
.credits button {
|
||||
margin-top: 20px;
|
||||
padding: 10px 20px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="turn-indicator" id="turnIndicator"></div>
|
||||
<div class="game-status" id="gameStatus"></div>
|
||||
<div class="game-container">
|
||||
<div id="chessboard"></div>
|
||||
<div class="scoreboard">
|
||||
<h2>Scoreboard</h2>
|
||||
<div class="score" id="whiteScore">White: 0</div>
|
||||
<div class="score" id="blackScore">Black: 0</div>
|
||||
<div class="leaderboard">
|
||||
<h3>Leaderboard</h3>
|
||||
<ol id="leaderboardList"></ol>
|
||||
</div>
|
||||
<div class="winner-input" id="winnerInput">
|
||||
<input type="text" id="winnerName" placeholder="Enter your name">
|
||||
<button onclick="saveWinner()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="credits" id="credits">
|
||||
<h1>Congratulations!</h1>
|
||||
<p>Game created by WebCraft Maestro, prompted by ParisNeo on lollms system.</p>
|
||||
<button onclick="playAgain()">Play Again</button>
|
||||
<audio id="creditMusic" src="https://www.bensound.com/bensound-music/bensound-sunny.mp3" loop></audio>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const board = document.getElementById('chessboard');
|
||||
const turnIndicator = document.getElementById('turnIndicator');
|
||||
const gameStatus = document.getElementById('gameStatus');
|
||||
const whiteScoreElement = document.getElementById('whiteScore');
|
||||
const blackScoreElement = document.getElementById('blackScore');
|
||||
const leaderboardList = document.getElementById('leaderboardList');
|
||||
const winnerInput = document.getElementById('winnerInput');
|
||||
const winnerNameInput = document.getElementById('winnerName');
|
||||
const credits = document.getElementById('credits');
|
||||
const creditMusic = document.getElementById('creditMusic');
|
||||
let selectedPiece = null;
|
||||
let currentPlayer = 'white';
|
||||
let whiteScore = 0;
|
||||
let blackScore = 0;
|
||||
let leaderboard = [];
|
||||
let gameBoard = [];
|
||||
|
||||
const initialBoard = [
|
||||
['♜', '♞', '♝', '♛', '♚', '♝', '♞', '♜'],
|
||||
['♟', '♟', '♟', '♟', '♟', '♟', '♟', '♟'],
|
||||
['', '', '', '', '', '', '', ''],
|
||||
['', '', '', '', '', '', '', ''],
|
||||
['', '', '', '', '', '', '', ''],
|
||||
['', '', '', '', '', '', '', ''],
|
||||
['♙', '♙', '♙', '♙', '♙', '♙', '♙', '♙'],
|
||||
['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖']
|
||||
];
|
||||
|
||||
const pieceValues = {
|
||||
'♙': 1, '♟': 1, // Pawns
|
||||
'♘': 3, '♞': 3, // Knights
|
||||
'♗': 3, '♝': 3, // Bishops
|
||||
'♖': 5, '♜': 5, // Rooks
|
||||
'♕': 9, '♛': 9, // Queens
|
||||
'♔': 0, '♚': 0 // Kings (no point value)
|
||||
};
|
||||
|
||||
function createBoard() {
|
||||
for (let row = 0; row < 8; row++) {
|
||||
gameBoard[row] = [];
|
||||
for (let col = 0; col < 8; col++) {
|
||||
const square = document.createElement('div');
|
||||
square.className = `square ${(row + col) % 2 === 0 ? 'white' : 'black'}`;
|
||||
square.dataset.row = row;
|
||||
square.dataset.col = col;
|
||||
square.textContent = initialBoard[row][col];
|
||||
square.addEventListener('click', handleClick);
|
||||
board.appendChild(square);
|
||||
gameBoard[row][col] = initialBoard[row][col];
|
||||
}
|
||||
}
|
||||
updateTurnIndicator();
|
||||
}
|
||||
|
||||
function handleClick(event) {
|
||||
const square = event.target;
|
||||
const row = parseInt(square.dataset.row);
|
||||
const col = parseInt(square.dataset.col);
|
||||
|
||||
if (selectedPiece) {
|
||||
if (square.classList.contains('possible-move')) {
|
||||
const capturedPiece = square.textContent;
|
||||
const fromRow = parseInt(selectedPiece.dataset.row);
|
||||
const fromCol = parseInt(selectedPiece.dataset.col);
|
||||
|
||||
// Make the move
|
||||
movePiece(selectedPiece, square);
|
||||
updateGameBoard(fromRow, fromCol, row, col);
|
||||
|
||||
// Check for check and checkmate
|
||||
const oppositeColor = currentPlayer === 'white' ? 'black' : 'white';
|
||||
if (isInCheck(oppositeColor)) {
|
||||
if (isCheckmate(oppositeColor)) {
|
||||
gameStatus.textContent = `Checkmate! ${currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1)} wins!`;
|
||||
winnerInput.style.display = 'block';
|
||||
showCredits();
|
||||
} else {
|
||||
gameStatus.textContent = `${oppositeColor.charAt(0).toUpperCase() + oppositeColor.slice(1)} is in check!`;
|
||||
}
|
||||
} else {
|
||||
gameStatus.textContent = '';
|
||||
}
|
||||
|
||||
updateScore(capturedPiece);
|
||||
clearSelection();
|
||||
currentPlayer = oppositeColor;
|
||||
updateTurnIndicator();
|
||||
} else {
|
||||
clearSelection();
|
||||
selectPiece(square);
|
||||
}
|
||||
} else {
|
||||
selectPiece(square);
|
||||
}
|
||||
}
|
||||
|
||||
function selectPiece(square) {
|
||||
const piece = square.textContent;
|
||||
if (piece && ((currentPlayer === 'white' && piece.charCodeAt(0) >= 9812 && piece.charCodeAt(0) <= 9817) ||
|
||||
(currentPlayer === 'black' && piece.charCodeAt(0) >= 9818 && piece.charCodeAt(0) <= 9823))) {
|
||||
selectedPiece = square;
|
||||
square.classList.add('selected');
|
||||
showPossibleMoves(square);
|
||||
}
|
||||
}
|
||||
|
||||
function showPossibleMoves(square) {
|
||||
const piece = square.textContent;
|
||||
const row = parseInt(square.dataset.row);
|
||||
const col = parseInt(square.dataset.col);
|
||||
|
||||
clearPossibleMoves();
|
||||
|
||||
switch (piece) {
|
||||
case '♔': case '♚': // King
|
||||
showKingMoves(row, col);
|
||||
break;
|
||||
case '♕': case '♛': // Queen
|
||||
showQueenMoves(row, col);
|
||||
break;
|
||||
case '♖': case '♜': // Rook
|
||||
showRookMoves(row, col);
|
||||
break;
|
||||
case '♗': case '♝': // Bishop
|
||||
showBishopMoves(row, col);
|
||||
break;
|
||||
case '♘': case '♞': // Knight
|
||||
showKnightMoves(row, col);
|
||||
break;
|
||||
case '♙': case '♟': // Pawn
|
||||
showPawnMoves(row, col, piece === '♙');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function showKingMoves(row, col) {
|
||||
const directions = [
|
||||
[-1, -1], [-1, 0], [-1, 1],
|
||||
[0, -1], [0, 1],
|
||||
[1, -1], [1, 0], [1, 1]
|
||||
];
|
||||
for (const [dx, dy] of directions) {
|
||||
const newRow = row + dx;
|
||||
const newCol = col + dy;
|
||||
if (isValidMove(newRow, newCol)) {
|
||||
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||
if (isValidTarget(targetSquare) && !wouldBeInCheck(row, col, newRow, newCol)) {
|
||||
targetSquare.classList.add('possible-move');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showQueenMoves(row, col) {
|
||||
showRookMoves(row, col);
|
||||
showBishopMoves(row, col);
|
||||
}
|
||||
|
||||
function showRookMoves(row, col) {
|
||||
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
|
||||
for (const [dx, dy] of directions) {
|
||||
let newRow = row + dx;
|
||||
let newCol = col + dy;
|
||||
while (isValidMove(newRow, newCol)) {
|
||||
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||
if (isValidTarget(targetSquare)) {
|
||||
if (!wouldBeInCheck(row, col, newRow, newCol)) {
|
||||
targetSquare.classList.add('possible-move');
|
||||
}
|
||||
if (targetSquare.textContent !== '') break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
newRow += dx;
|
||||
newCol += dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showBishopMoves(row, col) {
|
||||
const directions = [[-1, -1], [-1, 1], [1, -1], [1, 1]];
|
||||
for (const [dx, dy] of directions) {
|
||||
let newRow = row + dx;
|
||||
let newCol = col + dy;
|
||||
while (isValidMove(newRow, newCol)) {
|
||||
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||
if (isValidTarget(targetSquare)) {
|
||||
if (!wouldBeInCheck(row, col, newRow, newCol)) {
|
||||
targetSquare.classList.add('possible-move');
|
||||
}
|
||||
if (targetSquare.textContent !== '') break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
newRow += dx;
|
||||
newCol += dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showKnightMoves(row, col) {
|
||||
const moves = [
|
||||
[-2, -1], [-2, 1], [-1, -2], [-1, 2],
|
||||
[1, -2], [1, 2], [2, -1], [2, 1]
|
||||
];
|
||||
for (const [dx, dy] of moves) {
|
||||
const newRow = row + dx;
|
||||
const newCol = col + dy;
|
||||
if (isValidMove(newRow, newCol)) {
|
||||
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||
if (isValidTarget(targetSquare) && !wouldBeInCheck(row, col, newRow, newCol)) {
|
||||
targetSquare.classList.add('possible-move');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showPawnMoves(row, col, isWhite) {
|
||||
const direction = isWhite ? -1 : 1;
|
||||
const startRow = isWhite ? 6 : 1;
|
||||
|
||||
// Move forward
|
||||
let newRow = row + direction;
|
||||
if (isValidMove(newRow, col)) {
|
||||
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${col}"]`);
|
||||
if (targetSquare.textContent === '' && !wouldBeInCheck(row, col, newRow, col)) {
|
||||
targetSquare.classList.add('possible-move');
|
||||
|
||||
// Double move from starting position
|
||||
if (row === startRow) {
|
||||
newRow = row + 2 * direction;
|
||||
const doubleSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${col}"]`);
|
||||
if (doubleSquare.textContent === '' && !wouldBeInCheck(row, col, newRow, col)) {
|
||||
doubleSquare.classList.add('possible-move');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Capture diagonally
|
||||
for (const dcol of [-1, 1]) {
|
||||
newRow = row + direction;
|
||||
const newCol = col + dcol;
|
||||
if (isValidMove(newRow, newCol)) {
|
||||
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||
if (targetSquare.textContent !== '' && isValidTarget(targetSquare) && !wouldBeInCheck(row, col, newRow, newCol)) {
|
||||
targetSquare.classList.add('possible-move');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isValidMove(row, col) {
|
||||
return row >= 0 && row < 8 && col >= 0 && col < 8;
|
||||
}
|
||||
|
||||
function isValidTarget(square) {
|
||||
const piece = square.textContent;
|
||||
if (!piece) return true;
|
||||
if (currentPlayer === 'white') {
|
||||
return piece.charCodeAt(0) >= 9818;
|
||||
} else {
|
||||
return piece.charCodeAt(0) <= 9817;
|
||||
}
|
||||
}
|
||||
|
||||
function clearPossibleMoves() {
|
||||
document.querySelectorAll('.possible-move').forEach(square => square.classList.remove('possible-move'));
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
if (selectedPiece) {
|
||||
selectedPiece.classList.remove('selected');
|
||||
selectedPiece = null;
|
||||
clearPossibleMoves();
|
||||
}
|
||||
}
|
||||
|
||||
function movePiece(fromSquare, toSquare) {
|
||||
toSquare.textContent = fromSquare.textContent;
|
||||
fromSquare.textContent = '';
|
||||
}
|
||||
|
||||
function updateGameBoard(fromRow, fromCol, toRow, toCol) {
|
||||
gameBoard[toRow][toCol] = gameBoard[fromRow][fromCol];
|
||||
gameBoard[fromRow][fromCol] = '';
|
||||
}
|
||||
|
||||
function updateTurnIndicator() {
|
||||
turnIndicator.textContent = `${currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1)}'s turn`;
|
||||
turnIndicator.style.color = currentPlayer;
|
||||
}
|
||||
|
||||
function updateScore(capturedPiece) {
|
||||
if (capturedPiece) {
|
||||
const points = pieceValues[capturedPiece] || 0;
|
||||
if (currentPlayer === 'white') {
|
||||
whiteScore += points;
|
||||
} else {
|
||||
blackScore += points;
|
||||
}
|
||||
whiteScoreElement.textContent = `White: ${whiteScore}`;
|
||||
blackScoreElement.textContent = `Black: ${blackScore}`;
|
||||
updateLeaderboard();
|
||||
}
|
||||
}
|
||||
|
||||
function updateLeaderboard() {
|
||||
leaderboard = [
|
||||
{ name: 'White', score: whiteScore },
|
||||
{ name: 'Black', score: blackScore },
|
||||
...leaderboard.filter(entry => entry.name !== 'White' && entry.name !== 'Black')
|
||||
];
|
||||
leaderboard.sort((a, b) => b.score - a.score);
|
||||
leaderboard = leaderboard.slice(0, 5); // Keep only top 5
|
||||
|
||||
leaderboardList.innerHTML = '';
|
||||
leaderboard.forEach(entry => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = `${entry.name}: ${entry.score}`;
|
||||
leaderboardList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
function isInCheck(color) {
|
||||
const king = color === 'white' ? '♔' : '♚';
|
||||
let kingPosition;
|
||||
|
||||
// Find the king's position
|
||||
for (let row = 0; row < 8; row++) {
|
||||
for (let col = 0; col < 8; col++) {
|
||||
if (gameBoard[row][col] === king) {
|
||||
kingPosition = { row, col };
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (kingPosition) break;
|
||||
}
|
||||
|
||||
// Check if any opponent's piece can attack the king
|
||||
for (let row = 0; row < 8; row++) {
|
||||
for (let col = 0; col < 8; col++) {
|
||||
const piece = gameBoard[row][col];
|
||||
if (piece && ((color === 'white' && piece.charCodeAt(0) >= 9818) || (color === 'black' && piece.charCodeAt(0) <= 9817))) {
|
||||
if (canAttack(row, col, kingPosition.row, kingPosition.col)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function canAttack(fromRow, fromCol, toRow, toCol) {
|
||||
const piece = gameBoard[fromRow][fromCol];
|
||||
switch (piece) {
|
||||
case '♔': case '♚': // King
|
||||
return Math.abs(fromRow - toRow) <= 1 && Math.abs(fromCol - toCol) <= 1;
|
||||
case '♕': case '♛': // Queen
|
||||
return canAttackStraight(fromRow, fromCol, toRow, toCol) || canAttackDiagonal(fromRow, fromCol, toRow, toCol);
|
||||
case '♖': case '♜': // Rook
|
||||
return canAttackStraight(fromRow, fromCol, toRow, toCol);
|
||||
case '♗': case '♝': // Bishop
|
||||
return canAttackDiagonal(fromRow, fromCol, toRow, toCol);
|
||||
case '♘': case '♞': // Knight
|
||||
const dx = Math.abs(fromRow - toRow);
|
||||
const dy = Math.abs(fromCol - toCol);
|
||||
return (dx === 2 && dy === 1) || (dx === 1 && dy === 2);
|
||||
case '♙': case '♟': // Pawn
|
||||
const direction = piece === '♙' ? -1 : 1;
|
||||
return (fromCol === toCol && fromRow + direction === toRow) ||
|
||||
(Math.abs(fromCol - toCol) === 1 && fromRow + direction === toRow);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function canAttackStraight(fromRow, fromCol, toRow, toCol) {
|
||||
if (fromRow === toRow) {
|
||||
const step = fromCol < toCol ? 1 : -1;
|
||||
for (let col = fromCol + step; col !== toCol; col += step) {
|
||||
if (gameBoard[fromRow][col] !== '') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (fromCol === toCol) {
|
||||
const step = fromRow < toRow ? 1 : -1;
|
||||
for (let row = fromRow + step; row !== toRow; row += step) {
|
||||
if (gameBoard[row][fromCol] !== '') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function canAttackDiagonal(fromRow, fromCol, toRow, toCol) {
|
||||
if (Math.abs(fromRow - toRow) === Math.abs(fromCol - toCol)) {
|
||||
const rowStep = fromRow < toRow ? 1 : -1;
|
||||
const colStep = fromCol < toCol ? 1 : -1;
|
||||
let row = fromRow + rowStep;
|
||||
let col = fromCol + colStep;
|
||||
while (row !== toRow && col !== toCol) {
|
||||
if (gameBoard[row][col] !== '') return false;
|
||||
row += rowStep;
|
||||
col += colStep;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function wouldBeInCheck(fromRow, fromCol, toRow, toCol) {
|
||||
const tempPiece = gameBoard[toRow][toCol];
|
||||
gameBoard[toRow][toCol] = gameBoard[fromRow][fromCol];
|
||||
gameBoard[fromRow][fromCol] = '';
|
||||
const inCheck = isInCheck(currentPlayer);
|
||||
gameBoard[fromRow][fromCol] = gameBoard[toRow][toCol];
|
||||
gameBoard[toRow][toCol] = tempPiece;
|
||||
return inCheck;
|
||||
}
|
||||
|
||||
function isCheckmate(color) {
|
||||
for (let fromRow = 0; fromRow < 8; fromRow++) {
|
||||
for (let fromCol = 0; fromCol < 8; fromCol++) {
|
||||
const piece = gameBoard[fromRow][fromCol];
|
||||
if (piece && ((color === 'white' && piece.charCodeAt(0) <= 9817) || (color === 'black' && piece.charCodeAt(0) >= 9818))) {
|
||||
for (let toRow = 0; toRow < 8; toRow++) {
|
||||
for (let toCol = 0; toCol < 8; toCol++) {
|
||||
if (canAttack(fromRow, fromCol, toRow, toCol) && !wouldBeInCheck(fromRow, fromCol, toRow, toCol)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function saveWinner() {
|
||||
const winnerName = winnerNameInput.value.trim();
|
||||
if (winnerName) {
|
||||
const winner = leaderboard.find(entry => entry.name === winnerName);
|
||||
if (winner) {
|
||||
winner.score += 1;
|
||||
} else {
|
||||
leaderboard.push({ name: winnerName, score: 1 });
|
||||
}
|
||||
updateLeaderboard();
|
||||
winnerInput.style.display = 'none';
|
||||
winnerNameInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showCredits() {
|
||||
credits.style.display = 'flex';
|
||||
creditMusic.play();
|
||||
}
|
||||
|
||||
function playAgain() {
|
||||
credits.style.display = 'none';
|
||||
creditMusic.pause();
|
||||
creditMusic.currentTime = 0;
|
||||
resetGame();
|
||||
}
|
||||
|
||||
function resetGame() {
|
||||
board.innerHTML = '';
|
||||
gameStatus.textContent = '';
|
||||
winnerInput.style.display = 'none';
|
||||
selectedPiece = null;
|
||||
currentPlayer = 'white';
|
||||
whiteScore = 0;
|
||||
blackScore = 0;
|
||||
gameBoard = [];
|
||||
createBoard();
|
||||
updateTurnIndicator();
|
||||
updateScore();
|
||||
}
|
||||
|
||||
createBoard();
|
||||
updateLeaderboard();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -8,6 +8,12 @@ import uuid
|
||||
import os
|
||||
import requests
|
||||
import yaml
|
||||
from lollms.security import check_access
|
||||
import os
|
||||
import subprocess
|
||||
import yaml
|
||||
import uuid
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
lollmsElfServer: LOLLMSWebUI = LOLLMSWebUI.get_instance()
|
||||
@ -76,25 +82,28 @@ async def get_app_code(app_name: str, auth: AuthRequest):
|
||||
|
||||
@router.post("/install/{app_name}")
|
||||
async def install_app(app_name: str, auth: AuthRequest):
|
||||
check_access(lollmsElfServer, auth.client_id)
|
||||
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
|
||||
|
||||
# Create the app directory
|
||||
app_path = lollmsElfServer.lollms_paths.apps_zoo_path / app_name
|
||||
app_path = lollmsElfServer.lollms_paths.apps_zoo_path/app_name # Adjust the path as needed
|
||||
os.makedirs(app_path, exist_ok=True)
|
||||
|
||||
# Define the URLs for the files to download
|
||||
files_to_download = {
|
||||
"icon.png": f"https://github.com/ParisNeo/lollms_apps_zoo/raw/main/{app_name}/icon.png",
|
||||
"description.yaml": f"https://raw.githubusercontent.com/ParisNeo/lollms_apps_zoo/main/{app_name}/description.yaml",
|
||||
"index.html": f"https://raw.githubusercontent.com/ParisNeo/lollms_apps_zoo/main/{app_name}/index.html"
|
||||
# Define the local paths for the files to copy
|
||||
files_to_copy = {
|
||||
"icon.png": REPO_DIR/app_name/"icon.png",
|
||||
"description.yaml": REPO_DIR/app_name/"description.yaml",
|
||||
"index.html": REPO_DIR/app_name/"index.html"
|
||||
}
|
||||
|
||||
# Download each file
|
||||
for file_name, url in files_to_download.items():
|
||||
response = requests.get(url)
|
||||
if response.status_code == 200:
|
||||
with open(app_path / file_name, 'wb') as f:
|
||||
f.write(response.content)
|
||||
# Copy each file from the local repo
|
||||
for file_name, local_path in files_to_copy.items():
|
||||
if local_path.exists():
|
||||
with open(local_path, 'rb') as src_file:
|
||||
with open(app_path/file_name, 'wb') as dest_file:
|
||||
dest_file.write(src_file.read())
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=f"{file_name} not found on GitHub")
|
||||
raise HTTPException(status_code=404, detail=f"{file_name} not found in the local repository")
|
||||
|
||||
return {"message": f"App {app_name} installed successfully."}
|
||||
|
||||
@ -108,28 +117,43 @@ async def uninstall_app(app_name: str, auth: AuthRequest):
|
||||
raise HTTPException(status_code=404, detail="App not found")
|
||||
|
||||
|
||||
@router.get("/github/apps")
|
||||
async def fetch_github_apps():
|
||||
github_repo_url = "https://api.github.com/repos/ParisNeo/lollms_apps_zoo/contents"
|
||||
response = requests.get(github_repo_url)
|
||||
REPO_URL = "https://github.com/ParisNeo/lollms_apps_zoo.git"
|
||||
|
||||
if response.status_code == 200:
|
||||
|
||||
def clone_repo():
|
||||
REPO_DIR = Path(lollmsElfServer.lollms_paths.personal_path) / "apps_zoo_repo"
|
||||
|
||||
# Check if the directory exists and if it is empty
|
||||
if REPO_DIR.exists():
|
||||
if any(REPO_DIR.iterdir()): # Check if the directory is not empty
|
||||
print(f"Directory {REPO_DIR} is not empty. Aborting clone.")
|
||||
return
|
||||
else:
|
||||
REPO_DIR.mkdir(parents=True, exist_ok=True) # Create the directory if it doesn't exist
|
||||
|
||||
# Clone the repository
|
||||
subprocess.run(["git", "clone", REPO_URL, str(REPO_DIR)], check=True)
|
||||
print(f"Repository cloned into {REPO_DIR}")
|
||||
|
||||
def pull_repo():
|
||||
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
|
||||
subprocess.run(["git", "-C", str(REPO_DIR), "pull"], check=True)
|
||||
|
||||
def load_apps_data():
|
||||
apps = []
|
||||
for item in response.json():
|
||||
if item['type'] == 'dir':
|
||||
app_name = item['name']
|
||||
description_url = f"https://api.github.com/repos/ParisNeo/lollms_apps_zoo/contents/{app_name}/description.yaml"
|
||||
icon_url = f"https://github.com/ParisNeo/lollms_apps_zoo/blob/main/{app_name}/icon.png?raw=true"
|
||||
|
||||
# Fetch description.yaml
|
||||
description_response = requests.get(description_url)
|
||||
description_data = {}
|
||||
if description_response.status_code == 200:
|
||||
description_data = yaml.safe_load(requests.get(description_url).text)
|
||||
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
|
||||
for item in os.listdir(REPO_DIR):
|
||||
item_path = os.path.join(REPO_DIR, item)
|
||||
if os.path.isdir(item_path):
|
||||
description_path = os.path.join(item_path, "description.yaml")
|
||||
icon_url = f"https://github.com/ParisNeo/lollms_apps_zoo/blob/main/{item}/icon.png?raw=true"
|
||||
|
||||
if os.path.exists(description_path):
|
||||
with open(description_path, 'r') as file:
|
||||
description_data = yaml.safe_load(file)
|
||||
apps.append(AppInfo(
|
||||
uid=str(uuid.uuid4()),
|
||||
name=app_name,
|
||||
name=item,
|
||||
icon=icon_url,
|
||||
description=description_data.get('description', ''),
|
||||
author=description_data.get('author', ''),
|
||||
@ -137,9 +161,19 @@ async def fetch_github_apps():
|
||||
model_name=description_data.get('model_name', ''),
|
||||
disclaimer=description_data.get('disclaimer', 'No disclaimer provided.')
|
||||
))
|
||||
return apps
|
||||
|
||||
|
||||
|
||||
@router.get("/github/apps")
|
||||
async def fetch_github_apps():
|
||||
try:
|
||||
clone_repo()
|
||||
pull_repo()
|
||||
apps = load_apps_data()
|
||||
return {"apps": apps}
|
||||
else:
|
||||
raise HTTPException(status_code=response.status_code, detail="Failed to fetch apps from GitHub")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/apps/{app_name}/icon")
|
||||
async def get_app_icon(app_name: str):
|
||||
|
1
lollms_apps_zoo
Submodule
1
lollms_apps_zoo
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 10ea733d4354e9cb3b293d7c6393c60e7dfb3504
|
8
web/dist/assets/index-0db1108c.css
vendored
Normal file
8
web/dist/assets/index-0db1108c.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
8
web/dist/assets/index-4578000a.css
vendored
8
web/dist/assets/index-4578000a.css
vendored
File diff suppressed because one or more lines are too long
4
web/dist/index.html
vendored
4
web/dist/index.html
vendored
@ -6,8 +6,8 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LoLLMS WebUI</title>
|
||||
<script type="module" crossorigin src="/assets/index-b768925e.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-4578000a.css">
|
||||
<script type="module" crossorigin src="/assets/index-11efc659.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index-0db1108c.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="app-zoo w-full">
|
||||
<button @click="fetchGithubApps" class="bg-green-500 text-white px-4 py-2 rounded mb-4">Refresh apps from github</button>
|
||||
<button @click="fetchGithubApps" class="bg-green-500 text-white px-4 py-2 rounded mb-4">Refresh apps from GitHub</button>
|
||||
<div v-if="loading" class="loading-animation">Loading...</div>
|
||||
<div class="app-list grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 w-full">
|
||||
<div
|
||||
v-for="app in combinedApps"
|
||||
@ -8,7 +9,7 @@
|
||||
class="app-card border rounded-lg shadow-lg p-4 cursor-pointer hover:shadow-xl transition"
|
||||
@click="handleAppClick(app)"
|
||||
>
|
||||
<img :src="app.icon" alt="App Icon" class="w-16 h-16 mx-auto mb-2" />
|
||||
<img :src="app.icon" alt="App Icon" class="app-icon w-16 h-16 mx-auto mb-2 rounded-full border border-gray-300" />
|
||||
<p class="text-center font-semibold">{{ app.name }}</p>
|
||||
<p class="text-center text-sm text-gray-600">Author: {{ app.author }}</p>
|
||||
<p class="text-center text-sm text-gray-600">Version: {{ app.version }}</p>
|
||||
@ -28,6 +29,7 @@
|
||||
<iframe v-if="appCode" :srcdoc="appCode" class="app-frame w-full h-full border-none"></iframe>
|
||||
<p v-else class="text-center text-red-500">Please install this app to view its code.</p>
|
||||
</div>
|
||||
<div v-if="message" class="message" :class="{ success: successMessage, error: !successMessage }">{{ message }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -41,6 +43,9 @@ export default {
|
||||
githubApps: [],
|
||||
selectedApp: null,
|
||||
appCode: '',
|
||||
loading: false,
|
||||
message: '',
|
||||
successMessage: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -59,13 +64,28 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async fetchApps() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await axios.get('/apps');
|
||||
this.apps = response.data;
|
||||
this.showMessage('Refresh successful!', true);
|
||||
} catch (error) {
|
||||
this.showMessage('Failed to refresh apps.', false);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async fetchGithubApps() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await axios.get('/github/apps');
|
||||
this.githubApps = response.data.apps;
|
||||
this.fetchApps(); // Refresh the app list after fetching GitHub apps
|
||||
await this.fetchApps(); // Refresh the app list after fetching GitHub apps
|
||||
} catch (error) {
|
||||
this.showMessage('Failed to refresh GitHub apps.', false);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async handleAppClick(app) {
|
||||
if (app.installed) {
|
||||
@ -83,18 +103,41 @@ export default {
|
||||
this.appCode = '';
|
||||
},
|
||||
async installApp(appName) {
|
||||
this.loading = true;
|
||||
try {
|
||||
await axios.post(`/install/${appName}`, {
|
||||
client_id: this.$store.state.client_id,
|
||||
});
|
||||
this.showMessage('Installation succeeded!', true);
|
||||
} catch (error) {
|
||||
this.showMessage('Installation failed.', false);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.fetchApps(); // Refresh the app list
|
||||
this.fetchGithubApps(); // Refresh GitHub apps
|
||||
}
|
||||
},
|
||||
async uninstallApp(appName) {
|
||||
this.loading = true;
|
||||
try {
|
||||
await axios.post(`/uninstall/${appName}`, {
|
||||
client_id: this.$store.state.client_id,
|
||||
});
|
||||
this.showMessage('Uninstallation succeeded!', true);
|
||||
} catch (error) {
|
||||
this.showMessage('Uninstallation failed.', false);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.fetchApps(); // Refresh the app list
|
||||
}
|
||||
},
|
||||
showMessage(msg, success) {
|
||||
this.message = msg;
|
||||
this.successMessage = success;
|
||||
setTimeout(() => {
|
||||
this.message = '';
|
||||
}, 3000); // Clear message after 3 seconds
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchGithubApps(); // Fetch GitHub apps when the component is mounted
|
||||
@ -115,9 +158,41 @@ export default {
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
.app-icon {
|
||||
border-radius: 50%; /* Rounded edges */
|
||||
border: 2px solid #d1d5db; /* Light gray border */
|
||||
}
|
||||
.app-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
.loading-animation {
|
||||
animation: fadeIn 1s ease-in-out;
|
||||
margin: 20px;
|
||||
font-size: 1.5rem;
|
||||
color: #4a5568; /* Gray color */
|
||||
}
|
||||
.message {
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
.message.success {
|
||||
background-color: #c6f6d5; /* Green background */
|
||||
color: #2f855a; /* Dark green text */
|
||||
}
|
||||
.message.error {
|
||||
background-color: #fed7d7; /* Red background */
|
||||
color: #c53030; /* Dark red text */
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user