š“ Dame ā«
You (Red) 12
Computer (Black) 12
Your turn - Select a piece
š¤ Computer thinking...
checkers.php
PHP
heckers/Draughts game where users can play against the computer
<?php
/**
* Plugin Name: Checkers Game
* Plugin URI: https://it-breeze.info/
* Description: A fully functional Checkers/Draughts game where users can play against the computer. Use shortcode [checkers_game] to display the game.
* Version: 2.0.3
* Author: Mike Vahldieck
* Author URI: https://it-breeze.info/
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
<?php
/**
* Plugin Name: Checkers Game
* Plugin URI: https://it-breeze.info/
* Description: A fully functional Checkers/Draughts game where users can play against the computer. Use shortcode [checkers_game] to display the game.
* Version: 2.0.3
* Author: Mike Vahldieck
* Author URI: https://it-breeze.info/
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class CheckersPlugin {
public function __construct() {
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
add_shortcode('checkers_game', array($this, 'display_game'));
add_action('admin_menu', array($this, 'add_admin_menu'));
}
public function enqueue_scripts() {
// Check if shortcode exists on current page
global $post;
if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'checkers_game')) {
// Enqueue inline styles and scripts
add_action('wp_footer', array($this, 'add_inline_assets'));
}
}
public function add_inline_assets() {
?>
<style>
.checkers-container {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #8b4513 0%, #d2691e 100%);
padding: 25px;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
text-align: center;
max-width: 650px;
margin: 20px auto;
}
.checkers-wrapper {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 25px;
}
.checkers-title {
color: #333;
margin-bottom: 20px;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.checkers-info {
display: flex;
justify-content: space-around;
margin-bottom: 20px;
padding: 15px;
background: rgba(0, 0, 0, 0.05);
border-radius: 10px;
}
.checkers-player {
display: flex;
align-items: center;
gap: 10px;
font-size: 1.2em;
font-weight: bold;
}
.checkers-preview {
width: 30px;
height: 30px;
border-radius: 50%;
border: 3px solid #333;
position: relative;
}
.checkers-red-piece {
background: radial-gradient(circle at 30% 30%, #dc3545, #a71e2a);
}
.checkers-black-piece {
background: radial-gradient(circle at 30% 30%, #495057, #212529);
}
.checkers-turn {
background: rgba(40, 167, 69, 0.2);
padding: 12px 20px;
border-radius: 10px;
margin-bottom: 20px;
font-size: 1.3em;
font-weight: bold;
color: #333;
}
.checkers-board {
display: grid;
grid-template-columns: repeat(8, 60px);
grid-template-rows: repeat(8, 60px);
gap: 2px;
background: #8b4513;
padding: 15px;
border-radius: 15px;
margin: 20px auto;
justify-content: center;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2);
}
.checkers-square {
width: 60px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
position: relative;
transition: all 0.3s ease;
}
.checkers-light {
background: #f5deb3;
}
.checkers-dark {
background: #8b4513;
}
.checkers-square:hover {
transform: scale(1.05);
z-index: 10;
}
.checkers-piece {
width: 50px;
height: 50px;
border-radius: 50%;
border: 3px solid #333;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
.checkers-piece:hover {
transform: scale(1.1);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
}
.checkers-piece.checkers-red {
background: radial-gradient(circle at 30% 30%, #dc3545, #a71e2a);
}
.checkers-piece.checkers-black {
background: radial-gradient(circle at 30% 30%, #495057, #212529);
}
.checkers-piece.checkers-king::after {
content: 'ā';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ffd700;
font-size: 24px;
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
}
.checkers-selected {
background: rgba(255, 193, 7, 0.6) !important;
transform: scale(1.1);
box-shadow: 0 0 20px rgba(255, 193, 7, 0.8);
}
.checkers-valid-move {
background: rgba(40, 167, 69, 0.6) !important;
animation: checkers-pulse 1.5s ease-in-out infinite;
}
@keyframes checkers-pulse {
0%, 100% { opacity: 0.6; }
50% { opacity: 1; }
}
.checkers-controls {
margin-top: 25px;
}
.checkers-btn {
background: linear-gradient(45deg, #6f42c1, #5a2d91);
color: white;
border: none;
padding: 15px 30px;
font-size: 16px;
border-radius: 25px;
cursor: pointer;
margin: 0 10px;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(111, 66, 193, 0.3);
}
.checkers-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(111, 66, 193, 0.4);
}
.checkers-result {
background: linear-gradient(45deg, #20c997, #17a2b8);
padding: 20px;
border-radius: 15px;
margin-top: 20px;
font-size: 1.4em;
font-weight: bold;
color: white;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.checkers-thinking {
color: #e83e8c;
font-style: italic;
margin-top: 15px;
font-size: 1.2em;
}
.checkers-score {
font-size: 1.1em;
margin-left: 10px;
color: #6c757d;
}
@media (max-width: 600px) {
.checkers-board {
grid-template-columns: repeat(8, 45px);
grid-template-rows: repeat(8, 45px);
padding: 10px;
}
.checkers-square { width: 45px; height: 45px; }
.checkers-piece { width: 38px; height: 38px; }
.checkers-title { font-size: 2em; }
.checkers-info { flex-direction: column; gap: 10px; }
}
</style>
<script>
var CHECKERS = {
EMPTY: 0,
RED: 1, // Player
BLACK: 2, // Computer
RED_KING: 3,
BLACK_KING: 4,
board: [],
currentPlayer: 1,
selectedSquare: null,
validMoves: [],
gameOver: false,
mustJump: false,
init: function() {
this.board = [];
this.currentPlayer = this.RED; // Player goes first
this.selectedSquare = null;
this.validMoves = [];
this.gameOver = false;
this.mustJump = false;
// Initialize empty board
for (var row = 0; row < 8; row++) {
this.board[row] = [];
for (var col = 0; col < 8; col++) {
this.board[row][col] = this.EMPTY;
}
}
// Set up starting positions
this.setupBoard();
this.createBoard();
this.updateDisplay();
},
setupBoard: function() {
// Place black pieces (computer) on top 3 rows
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 8; col++) {
if ((row + col) % 2 === 1) { // Dark squares only
this.board[row][col] = this.BLACK;
}
}
}
// Place red pieces (player) on bottom 3 rows
for (var row = 5; row < 8; row++) {
for (var col = 0; col < 8; col++) {
if ((row + col) % 2 === 1) { // Dark squares only
this.board[row][col] = this.RED;
}
}
}
},
createBoard: function() {
var boardEl = document.getElementById('checkers-board');
if (!boardEl) return;
boardEl.innerHTML = '';
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 8; col++) {
var square = document.createElement('div');
square.className = 'checkers-square ' +
((row + col) % 2 === 0 ? 'checkers-light' : 'checkers-dark');
square.id = 'square-' + row + '-' + col;
square.onclick = (function(r, c) {
return function() { CHECKERS.handleSquareClick(r, c); };
})(row, col);
boardEl.appendChild(square);
}
}
},
updateDisplay: function() {
this.clearHighlights();
// Update pieces on board
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 8; col++) {
var square = document.getElementById('square-' + row + '-' + col);
if (square) {
square.innerHTML = '';
var piece = this.board[row][col];
if (piece !== this.EMPTY) {
var pieceEl = document.createElement('div');
pieceEl.className = 'checkers-piece';
if (piece === this.RED || piece === this.RED_KING) {
pieceEl.classList.add('checkers-red');
} else {
pieceEl.classList.add('checkers-black');
}
if (piece === this.RED_KING || piece === this.BLACK_KING) {
pieceEl.classList.add('checkers-king');
}
square.appendChild(pieceEl);
}
}
}
}
// Highlight selected square and valid moves
if (this.selectedSquare) {
var selSquare = document.getElementById('square-' + this.selectedSquare.row + '-' + this.selectedSquare.col);
if (selSquare) selSquare.classList.add('checkers-selected');
}
for (var i = 0; i < this.validMoves.length; i++) {
var move = this.validMoves[i];
var moveSquare = document.getElementById('square-' + move.toRow + '-' + move.toCol);
if (moveSquare) moveSquare.classList.add('checkers-valid-move');
}
this.updateTurnInfo();
this.updateScore();
},
clearHighlights: function() {
var elements = document.querySelectorAll('.checkers-selected, .checkers-valid-move');
for (var i = 0; i < elements.length; i++) {
elements[i].classList.remove('checkers-selected', 'checkers-valid-move');
}
},
updateTurnInfo: function() {
var turnEl = document.getElementById('checkers-turn');
var thinkEl = document.getElementById('checkers-thinking');
if (this.gameOver) return;
if (this.currentPlayer === this.RED) {
if (turnEl) {
turnEl.textContent = 'Your turn' + (this.mustJump ? ' - You must jump!' : ' - Select a piece');
turnEl.style.display = 'block';
}
if (thinkEl) thinkEl.style.display = 'none';
} else {
if (turnEl) turnEl.style.display = 'none';
if (thinkEl) thinkEl.style.display = 'block';
}
},
updateScore: function() {
var redCount = 0, blackCount = 0;
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 8; col++) {
var piece = this.board[row][col];
if (piece === this.RED || piece === this.RED_KING) redCount++;
if (piece === this.BLACK || piece === this.BLACK_KING) blackCount++;
}
}
var redScore = document.getElementById('checkers-red-score');
var blackScore = document.getElementById('checkers-black-score');
if (redScore) redScore.textContent = redCount;
if (blackScore) blackScore.textContent = blackCount;
},
handleSquareClick: function(row, col) {
if (this.gameOver || this.currentPlayer !== this.RED) return;
var piece = this.board[row][col];
// If clicking on own piece, select it
if (piece === this.RED || piece === this.RED_KING) {
this.selectPiece(row, col);
}
// If clicking on valid move square, make the move
else if (this.selectedSquare && this.isValidMove(row, col)) {
this.makeMove(this.selectedSquare.row, this.selectedSquare.col, row, col);
}
// If clicking elsewhere, deselect
else {
this.selectedSquare = null;
this.validMoves = [];
this.updateDisplay();
}
},
selectPiece: function(row, col) {
this.selectedSquare = {row: row, col: col};
this.validMoves = this.getValidMoves(row, col);
this.updateDisplay();
},
isValidMove: function(toRow, toCol) {
for (var i = 0; i < this.validMoves.length; i++) {
var move = this.validMoves[i];
if (move.toRow === toRow && move.toCol === toCol) {
return true;
}
}
return false;
},
getValidMoves: function(row, col) {
var moves = [];
var piece = this.board[row][col];
if (piece === this.EMPTY) return moves;
var isKing = (piece === this.RED_KING || piece === this.BLACK_KING);
var isRed = (piece === this.RED || piece === this.RED_KING);
// Determine movement directions
var directions = [];
if (isKing) {
directions = [[-1, -1], [-1, 1], [1, -1], [1, 1]]; // All directions
} else if (isRed) {
directions = [[-1, -1], [-1, 1]]; // Red moves up
} else {
directions = [[1, -1], [1, 1]]; // Black moves down
}
// Check for jumps first
var jumpMoves = [];
for (var i = 0; i < directions.length; i++) {
var dr = directions[i][0];
var dc = directions[i][1];
var jumpMove = this.getJumpMove(row, col, dr, dc);
if (jumpMove) jumpMoves.push(jumpMove);
}
// If jumps available, only return jumps (forced jumps)
if (jumpMoves.length > 0) {
return jumpMoves;
}
// Otherwise, check for regular moves
for (var i = 0; i < directions.length; i++) {
var dr = directions[i][0];
var dc = directions[i][1];
var newRow = row + dr;
var newCol = col + dc;
if (this.isInBounds(newRow, newCol) && this.board[newRow][newCol] === this.EMPTY) {
moves.push({
fromRow: row, fromCol: col,
toRow: newRow, toCol: newCol,
isJump: false
});
}
}
return moves;
},
getJumpMove: function(row, col, dr, dc) {
var middleRow = row + dr;
var middleCol = col + dc;
var landRow = row + (dr * 2);
var landCol = col + (dc * 2);
if (!this.isInBounds(middleRow, middleCol) || !this.isInBounds(landRow, landCol)) {
return null;
}
var piece = this.board[row][col];
var middlePiece = this.board[middleRow][middleCol];
var landPiece = this.board[landRow][landCol];
// Must jump over opponent piece to empty square
if (landPiece === this.EMPTY && this.isOpponent(piece, middlePiece)) {
return {
fromRow: row, fromCol: col,
toRow: landRow, toCol: landCol,
jumpRow: middleRow, jumpCol: middleCol,
isJump: true
};
}
return null;
},
isOpponent: function(piece1, piece2) {
if (piece1 === this.EMPTY || piece2 === this.EMPTY) return false;
var isRed1 = (piece1 === this.RED || piece1 === this.RED_KING);
var isRed2 = (piece2 === this.RED || piece2 === this.RED_KING);
return isRed1 !== isRed2;
},
isInBounds: function(row, col) {
return row >= 0 && row < 8 && col >= 0 && col < 8;
},
makeMove: function(fromRow, fromCol, toRow, toCol) {
var move = null;
for (var i = 0; i < this.validMoves.length; i++) {
if (this.validMoves[i].toRow === toRow && this.validMoves[i].toCol === toCol) {
move = this.validMoves[i];
break;
}
}
if (!move) return false;
// Move piece
var piece = this.board[fromRow][fromCol];
this.board[fromRow][fromCol] = this.EMPTY;
this.board[toRow][toCol] = piece;
// Handle jump
if (move.isJump) {
this.board[move.jumpRow][move.jumpCol] = this.EMPTY;
}
// Promote to king
this.checkPromotion(toRow, toCol);
// Clear selection
this.selectedSquare = null;
this.validMoves = [];
// Check for game over
if (this.checkGameOver()) {
return true;
}
// Switch turns only for player moves
if (this.currentPlayer === this.RED) {
this.currentPlayer = this.BLACK;
this.updateDisplay();
// Computer move
var self = this;
setTimeout(function() { self.computerMove(); }, 1000);
} else {
// This shouldn't happen with the new computer move logic
this.currentPlayer = this.RED;
this.updateDisplay();
}
return true;
},
checkPromotion: function(row, col) {
var piece = this.board[row][col];
if (piece === this.RED && row === 0) {
this.board[row][col] = this.RED_KING;
} else if (piece === this.BLACK && row === 7) {
this.board[row][col] = this.BLACK_KING;
}
},
computerMove: function() {
if (this.gameOver || this.currentPlayer !== this.BLACK) return;
var allMoves = this.getAllMoves(this.BLACK);
if (allMoves.length === 0) {
this.endGame('š You won! Computer has no moves.');
return;
}
// Simple AI: prefer jumps, then random moves
var jumpMoves = [];
for (var i = 0; i < allMoves.length; i++) {
if (allMoves[i].isJump) {
jumpMoves.push(allMoves[i]);
}
}
var movesToConsider = jumpMoves.length > 0 ? jumpMoves : allMoves;
var randomMove = movesToConsider[Math.floor(Math.random() * movesToConsider.length)];
// Execute the move
var piece = this.board[randomMove.fromRow][randomMove.fromCol];
this.board[randomMove.fromRow][randomMove.fromCol] = this.EMPTY;
this.board[randomMove.toRow][randomMove.toCol] = piece;
// Handle jump
if (randomMove.isJump) {
this.board[randomMove.jumpRow][randomMove.jumpCol] = this.EMPTY;
}
// Promote to king
this.checkPromotion(randomMove.toRow, randomMove.toCol);
// Check for game over
if (this.checkGameOver()) {
return;
}
// Switch back to player
this.currentPlayer = this.RED;
this.updateDisplay();
},
getAllMoves: function(player) {
var allMoves = [];
for (var row = 0; row < 8; row++) {
for (var col = 0; col < 8; col++) {
var piece = this.board[row][col];
if ((player === this.RED && (piece === this.RED || piece === this.RED_KING)) ||
(player === this.BLACK && (piece === this.BLACK || piece === this.BLACK_KING))) {
var moves = this.getValidMoves(row, col);
allMoves = allMoves.concat(moves);
}
}
}
return allMoves;
},
checkGameOver: function() {
var redMoves = this.getAllMoves(this.RED);
var blackMoves = this.getAllMoves(this.BLACK);
if (redMoves.length === 0) {
this.endGame('š¤ Computer won! You have no moves.');
return true;
} else if (blackMoves.length === 0) {
this.endGame('š You won! Computer has no moves.');
return true;
}
return false;
},
endGame: function(message) {
this.gameOver = true;
var resultEl = document.getElementById('checkers-result');
if (resultEl) {
resultEl.textContent = message;
resultEl.style.display = 'block';
}
var turnEl = document.getElementById('checkers-turn');
var thinkEl = document.getElementById('checkers-thinking');
if (turnEl) turnEl.style.display = 'none';
if (thinkEl) thinkEl.style.display = 'none';
},
newGame: function() {
var resultEl = document.getElementById('checkers-result');
var thinkEl = document.getElementById('checkers-thinking');
if (resultEl) resultEl.style.display = 'none';
if (thinkEl) thinkEl.style.display = 'none';
this.init();
},
showRules: function() {
alert('š“ Dame Rules ā«\\n\\nšÆ Capture all opponent pieces or block their moves!\\n\\nš Basic Rules:\\n⢠Move diagonally on dark squares only\\n⢠Jump over opponent pieces to capture them\\n⢠Must take jumps when available\\n⢠Reach the far end to become a King\\n⢠Kings can move backward\\n\\nš Win by:\\n⢠Capturing all opponent pieces\\n⢠Blocking all opponent moves\\n\\nGood luck! š');
}
};
// Initialize when page loads
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
if (document.getElementById('checkers-board')) {
CHECKERS.init();
}
}, 200);
});
} else {
setTimeout(function() {
if (document.getElementById('checkers-board')) {
CHECKERS.init();
}
}, 200);
}
</script>
<?php
}
public function display_game($atts) {
$atts = shortcode_atts(array(
'width' => '100%',
'height' => 'auto'
), $atts, 'checkers_game');
ob_start();
?>
<div class="checkers-container" style="width: <?php echo esc_attr($atts['width']); ?>; height: <?php echo esc_attr($atts['height']); ?>;">
<div class="checkers-wrapper">
<h3 class="checkers-title">š“ Dame ā«</h3>
<div class="checkers-info">
<div class="checkers-player">
<div class="checkers-preview checkers-red-piece"></div>
<span>You (Red) <span class="checkers-score" id="checkers-red-score">12</span></span>
</div>
<div class="checkers-player">
<div class="checkers-preview checkers-black-piece"></div>
<span>Computer (Black) <span class="checkers-score" id="checkers-black-score">12</span></span>
</div>
</div>
<div id="checkers-turn" class="checkers-turn">Your turn - Select a piece</div>
<div id="checkers-thinking" class="checkers-thinking" style="display: none;">š¤ Computer thinking...</div>
<div class="checkers-board" id="checkers-board"></div>
<div class="checkers-controls">
<button class="checkers-btn" onclick="CHECKERS.newGame()">š® New Game</button>
<button class="checkers-btn" onclick="CHECKERS.showRules()">š Rules</button>
</div>
<div id="checkers-result" class="checkers-result" style="display: none;"></div>
</div>
</div>
<?php
return ob_get_clean();
}
public function add_admin_menu() {
add_options_page(
'Checkers Game Settings',
'Checkers Game',
'manage_options',
'checkers-settings',
array($this, 'admin_page')
);
}
public function admin_page() {
?>
<div class="wrap">
<h1>Checkers Game Settings</h1>
<div class="card">
<h2>How to Use</h2>
<p>To display the Checkers game on any post or page, use the following shortcode:</p>
<code>[checkers_game]</code>
<h3>Shortcode Options</h3>
<ul>
<li><strong>width</strong> - Set the game container width (default: 100%)</li>
<li><strong>height</strong> - Set the game container height (default: auto)</li>
</ul>
<h3>Examples</h3>
<p><code>[checkers_game width="650px"]</code></p>
<p><code>[checkers_game width="100%" height="800px"]</code></p>
</div>
<div class="card">
<h2>Game Rules</h2>
<ol>
<li><strong>Objective:</strong> Capture all opponent pieces or block their moves</li>
<li><strong>Movement:</strong> Move diagonally on dark squares only</li>
<li><strong>Capturing:</strong> Jump over opponent pieces to capture them</li>
<li><strong>Forced Jumps:</strong> Must take jumps when available</li>
<li><strong>King Promotion:</strong> Pieces become Kings when reaching the far end</li>
<li><strong>King Movement:</strong> Kings can move and capture backward</li>
<li><strong>Winning:</strong> Capture all pieces or block all opponent moves</li>
</ol>
</div>
<div class="card">
<h2>Features</h2>
<ul>
<li>ā
Full 8x8 checkers board with proper alternating colors</li>
<li>ā
Authentic checkers gameplay with forced jumps</li>
<li>ā
King promotion when pieces reach the opposite end</li>
<li>ā
Smart computer opponent that prefers capturing moves</li>
<li>ā
Visual highlighting of selected pieces and valid moves</li>
<li>ā
Real-time piece counting and score tracking</li>
<li>ā
Responsive design for mobile and desktop</li>
<li>ā
Smooth animations and hover effects</li>
<li>ā
Proper win/lose detection</li>
</ul>
</div>
<div class="card">
<h2>Gameplay Tips</h2>
<ul>
<li><strong>Control the Center:</strong> Central pieces have more movement options</li>
<li><strong>Advance Carefully:</strong> Don't rush forward without support</li>
<li><strong>Create Kings:</strong> Kings are much more powerful than regular pieces</li>
<li><strong>Force Trades:</strong> When ahead, trade pieces to simplify the position</li>
<li><strong>Block Advancement:</strong> Prevent opponent pieces from becoming Kings</li>
</ul>
</div>
</div>
<?php
}
}
// Initialize the plugin
new CheckersPlugin();
// Add settings link on plugin page
if (is_admin()) {
add_filter('plugin_action_links_' . plugin_basename(__FILE__), function($links) {
$settings_link = '<a href="' . admin_url('options-general.php?page=checkers-settings') . '">Settings</a>';
array_unshift($links, $settings_link);
return $links;
});
}