Josh Bruce Online

Chaseopoly - Custom Monopoly Game Documentation

Overview

“Chaseopoly” is a fully-featured custom implementation of the classic Monopoly board game, themed around Chase School subjects and locations. The application provides a complete digital board game experience with multiplayer support (2-4 players), property management, chance/community chest cards, and comprehensive game mechanics including jail, taxes, and utilities.

Technical Architecture

Core Technologies

File Structure

monopoly/
└── index.html    # Complete Monopoly game implementation (801 lines)

Game Board Architecture

CSS Grid-Based Board Layout

.board {
  position: relative;
  width: 100%;
  aspect-ratio: 1;                    /* Perfect square board */
  background: #fafafa;
  border: 2px solid #e0e0e0;
  display: grid;
  grid-template-columns: repeat(11, 1fr);    /* 11x11 grid system */
  grid-template-rows: repeat(11, 1fr);
}

Sophisticated Space Positioning System

/* Corner spaces (larger, 2x2 grid cells) */
.corner {
  grid-column: span 2;
  grid-row: span 2;
  font-size: 11px;
  font-weight: 600;
}

/* Precise board positioning classes */
.go { grid-column: 11; grid-row: 11; }
.jail { grid-column: 1; grid-row: 11; }
.free-parking { grid-column: 1; grid-row: 1; }
.go-to-jail { grid-column: 11; grid-row: 1; }

/* Side positioning (bottom, left, top, right) */
.bottom-1 { grid-column: 10; grid-row: 11; }
.left-1 { grid-column: 1; grid-row: 10; }
.top-1 { grid-column: 2; grid-row: 1; }
.right-1 { grid-column: 11; grid-row: 2; }

Custom Chase School Theme

Educational Property Names

The game features properties themed around Chase School subjects and locations:

const boardSpaces = [
  { name: 'GO', type: 'go', position: 0 },
  { name: 'Dance Studio', type: 'property', color: 'brown', price: 60, rent: 2 },
  { name: 'Music', type: 'property', color: 'brown', price: 60, rent: 4 },
  { name: 'Creative Media Corridor', type: 'station', price: 200, rent: 25 },
  { name: 'Performing Arts', type: 'property', color: 'light-blue', price: 100, rent: 6 },
  { name: 'Sociology', type: 'property', color: 'light-blue', price: 100, rent: 6 },
  { name: 'Psychology', type: 'property', color: 'light-blue', price: 120, rent: 8 },
  // ... continues with all Chase School subjects and locations
  { name: 'COOP', type: 'property', color: 'dark-blue', price: 400, rent: 50 }
];

Educational Utility and Station Names

Subject-Based Property Groups

/* Property color coding by subject area */
.brown { background: #8B4513; }        /* Arts subjects */
.light-blue { background: #5DADE2; }   /* Social sciences */
.pink { background: #EC7063; }         /* Creative subjects */
.orange { background: #F39C12; }       /* Humanities */
.red { background: #E74C3C; }          /* Mathematics */
.yellow { background: #F1C40F; }       /* Languages & Computing */
.green { background: #27AE60; }        /* Sciences */
.dark-blue { background: #2C3E50; }    /* Local businesses */

Game State Management System

Comprehensive Player Object

const player = {
  id: index + 1,
  name: 'Player Name',
  money: 1500,                  /* Starting money */
  position: 0,                  /* Board position (0-39) */
  properties: [],              /* Owned properties array */
  color: `player${index + 1}`,  /* CSS class for token color */
  inJail: false,               /* Jail status */
  jailTurns: 0                 /* Turns spent in jail */
};

Game State Architecture

const gameState = {
  players: [],                 /* Array of player objects */
  currentPlayer: 0,            /* Index of current player */
  spaces: [],                  /* Board spaces with ownership data */
  started: false,              /* Game initialization status */
  modalCallback: null          /* Current modal interaction */
};

Visual Design System

Modern Interface Design

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: #f5f5f5;         /* Light gray background */
  display: flex;
  justify-content: center;
  align-items: center;
  color: #222;
}

.game-container {
  background: white;
  padding: 40px;
  max-width: 1200px;
  width: 95%;
  display: flex;
  gap: 40px;                   /* Board and controls separation */
}

Interactive Board Spaces

.space {
  border: 1px solid #e0e0e0;
  background: white;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-size: 9px;
  text-align: center;
  padding: 2px;
  cursor: pointer;
  transition: transform 0.2s;
}

.space:hover {
  transform: scale(1.05);       /* Subtle hover effect */
  z-index: 10;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

Property Color Bar System

.property {
  position: relative;
}

.color-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 15%;                  /* Color indicator at top of property */
}

Player Token System

Dynamic Token Positioning

function positionToken(playerIndex, position) {
  const token = document.getElementById(`player${playerIndex + 1}`);
  const spaces = document.querySelectorAll('.space');
  const targetSpace = spaces[position];
  
  if (targetSpace) {
    const rect = targetSpace.getBoundingClientRect();
    const boardRect = document.getElementById('board').getBoundingClientRect();
    
    // Multiple player offset calculation
    const offsetX = (playerIndex % 2) * 15 - 7.5;      /* Horizontal offset */
    const offsetY = Math.floor(playerIndex / 2) * 15 - 7.5;  /* Vertical offset */
    
    token.style.left = `${rect.left - boardRect.left + rect.width/2 - 9 + offsetX}px`;
    token.style.top = `${rect.top - boardRect.top + rect.height/2 - 9 + offsetY}px`;
  }
}

Smooth Token Animation

.player-token {
  position: absolute;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  transition: all 0.5s ease;    /* Smooth movement animation */
  z-index: 20;
  border: 2px solid white;
}

/* Player color system */
.player1 { background: #E74C3C; }  /* Red */
.player2 { background: #3498DB; }  /* Blue */
.player3 { background: #2ECC71; }  /* Green */
.player4 { background: #F39C12; }  /* Orange */

Game Mechanics Implementation

Dice Rolling System

function rollDice() {
  const dice1 = Math.floor(Math.random() * 6) + 1;
  const dice2 = Math.floor(Math.random() * 6) + 1;
  const total = dice1 + dice2;
  
  document.getElementById('dice1').textContent = dice1;
  document.getElementById('dice2').textContent = dice2;
  
  const currentPlayer = gameState.players[gameState.currentPlayer];
  
  // Handle jail mechanics
  if (currentPlayer.inJail) {
    if (dice1 === dice2) {
      // Doubles get out of jail free
      currentPlayer.inJail = false;
      currentPlayer.jailTurns = 0;
    } else {
      currentPlayer.jailTurns++;
      if (currentPlayer.jailTurns >= 3) {
        // Forced payment after 3 turns
        currentPlayer.money -= 50;
        currentPlayer.inJail = false;
        currentPlayer.jailTurns = 0;
      } else {
        nextTurn();
        return;
      }
    }
  }
  
  movePlayer(total);
}

Player Movement with GO Bonus

function movePlayer(spaces) {
  const currentPlayer = gameState.players[gameState.currentPlayer];
  const oldPosition = currentPlayer.position;
  currentPlayer.position = (currentPlayer.position + spaces) % 40;
  
  // Pass GO bonus detection
  if (currentPlayer.position < oldPosition) {
    currentPlayer.money += 200;    /* Collect £200 for passing GO */
  }
  
  positionToken(gameState.currentPlayer, currentPlayer.position);
  
  setTimeout(() => {
    handleLanding();              /* Process landing after animation */
  }, 500);
}

Property Management System

Property Purchase Logic

function handleProperty(space) {
  const currentPlayer = gameState.players[gameState.currentPlayer];
  
  if (!space.owner) {
    // Property available for purchase
    if (currentPlayer.money >= space.price) {
      showModal(
        `Buy ${space.name}?`,
        `Price: £${space.price}`,
        () => {
          // Purchase confirmed
          currentPlayer.money -= space.price;
          space.owner = currentPlayer.id;
          currentPlayer.properties.push(space);
          updateUI();
          hideModal();
          nextTurn();
        },
        () => {
          // Purchase declined
          hideModal();
          nextTurn();
        }
      );
    }
  } else if (space.owner !== currentPlayer.id) {
    // Pay rent to another player
    const owner = gameState.players.find(p => p.id === space.owner);
    let rent = space.rent;
    
    // Special utility rent calculation
    if (space.type === 'utility') {
      const dice1 = parseInt(document.getElementById('dice1').textContent);
      const dice2 = parseInt(document.getElementById('dice2').textContent);
      rent = (dice1 + dice2) * 4;    /* Rent = dice roll × 4 */
    }
    
    currentPlayer.money -= rent;
    owner.money += rent;
    
    showModal(
      'Rent Due',
      `Pay £${rent} to ${owner.name}`,
      () => {
        hideModal();
        nextTurn();
      }
    );
  }
}

Chance and Community Chest System

Dynamic Card System

function handleChance() {
  const chances = [
    { 
      text: 'Advance to GO', 
      action: (player) => {
        player.position = 0;
        player.money += 200;          /* Collect GO bonus */
        positionToken(gameState.currentPlayer, 0);
      }
    },
    { 
      text: 'Bank pays dividend of £50', 
      action: (player) => {
        player.money += 50;
      }
    },
    { 
      text: 'Pay school fees of £150', 
      action: (player) => {
        player.money -= 150;
      }
    }
  ];
  
  const chance = chances[Math.floor(Math.random() * chances.length)];
  const currentPlayer = gameState.players[gameState.currentPlayer];
  
  showModal('Chance', chance.text, () => {
    chance.action(currentPlayer);
    hideModal();
    updateUI();
    nextTurn();
  });
}

Community Chest Implementation

function handleCommunityChest() {
  const chests = [
    { text: 'Collect £100', action: (player) => player.money += 100 },
    { text: 'Pay £50', action: (player) => player.money -= 50 },
    { text: 'Collect £10', action: (player) => player.money += 10 }
  ];
  
  const chest = chests[Math.floor(Math.random() * chests.length)];
  const currentPlayer = gameState.players[gameState.currentPlayer];
  
  showModal('Community Chest', chest.text, () => {
    chest.action(currentPlayer);
    hideModal();
    updateUI();
    nextTurn();
  });
}

Flexible Modal Framework

function showModal(title, text, onYes, onNo) {
  document.getElementById('modalTitle').textContent = title;
  document.getElementById('modalText').textContent = text;
  gameState.modalCallback = { yes: onYes, no: onNo || hideModal };
  
  const modal = document.getElementById('modal');
  modal.style.display = 'flex';
  
  // Dynamic button configuration
  if (!onNo) {
    // Single button (OK only)
    modal.querySelector('.modal-buttons').innerHTML = 
      '<button class="btn" onclick="modalYes()">OK</button>';
  } else {
    // Two buttons (Yes/No)
    modal.querySelector('.modal-buttons').innerHTML = `
      <button class="btn" onclick="modalYes()">Yes</button>
      <button class="btn" onclick="modalNo()">No</button>
    `;
  }
}
.modal {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.3);     /* Semi-transparent overlay */
  z-index: 1000;
  justify-content: center;
  align-items: center;
}

.modal-content {
  background: white;
  padding: 30px;
  max-width: 400px;
  width: 90%;
  text-align: center;
}

Jail Mechanics System

Comprehensive Jail Implementation

function goToJail() {
  const currentPlayer = gameState.players[gameState.currentPlayer];
  currentPlayer.position = 10;         /* Jail position */
  currentPlayer.inJail = true;
  currentPlayer.jailTurns = 0;
  positionToken(gameState.currentPlayer, 10);
}

// Jail escape mechanics in rollDice()
if (currentPlayer.inJail) {
  if (dice1 === dice2) {
    // Doubles: immediate release
    currentPlayer.inJail = false;
    currentPlayer.jailTurns = 0;
  } else {
    currentPlayer.jailTurns++;
    if (currentPlayer.jailTurns >= 3) {
      // Forced payment after 3 failed attempts
      currentPlayer.money -= 50;
      currentPlayer.inJail = false;
      currentPlayer.jailTurns = 0;
    }
  }
}

User Interface Management

Real-Time UI Updates

function updateUI() {
  const currentPlayer = gameState.players[gameState.currentPlayer];
  document.getElementById('currentPlayerName').textContent = currentPlayer.name;
  document.getElementById('playerMoney').textContent = currentPlayer.money;
  document.getElementById('playerPosition').textContent = 
    gameState.spaces[currentPlayer.position].name;
  
  updatePropertyList();
}

function updatePropertyList() {
  const currentPlayer = gameState.players[gameState.currentPlayer];
  const propertyList = document.getElementById('propertyList');
  propertyList.innerHTML = '';
  
  currentPlayer.properties.forEach(property => {
    const item = document.createElement('div');
    item.className = 'property-item';
    item.textContent = `${property.name} - £${property.price}`;
    propertyList.appendChild(item);
  });
}

Control Panel Design

.controls {
  flex: 0 0 350px;              /* Fixed width sidebar */
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.player-setup, .player-info, .dice-container, .property-list {
  background: #fafafa;
  padding: 20px;
  border: 1px solid #e0e0e0;
}

.dice {
  display: inline-block;
  width: 50px;
  height: 50px;
  background: white;
  border: 2px solid #333;
  margin: 10px;
  font-size: 28px;
  line-height: 46px;
  font-weight: 600;
}

Board Creation and Initialization

Dynamic Board Generation

function createBoard() {
  const board = document.getElementById('board');
  const positions = [
    'go', 'bottom-1', 'bottom-2', 'bottom-3', 'bottom-4', 'bottom-5', 
    'bottom-6', 'bottom-7', 'bottom-8', 'bottom-9', 'jail',
    // ... all 40 positions
  ];

  boardSpaces.forEach((space, index) => {
    const spaceElement = document.createElement('div');
    spaceElement.className = `space ${positions[index]}`;
    
    // Corner space styling
    if ([0, 10, 20, 30].includes(index)) {
      spaceElement.classList.add('corner');
    }
    
    // Property color bar
    if (space.type === 'property' || space.type === 'station') {
      spaceElement.classList.add('property');
      if (space.color) {
        const colorBar = document.createElement('div');
        colorBar.className = `color-bar ${space.color}`;
        spaceElement.appendChild(colorBar);
      }
    }
    
    // Space name and price
    const nameDiv = document.createElement('div');
    nameDiv.textContent = space.name;
    nameDiv.style.fontSize = index % 10 === 0 ? '11px' : '9px';
    spaceElement.appendChild(nameDiv);
    
    if (space.price) {
      const priceDiv = document.createElement('div');
      priceDiv.textContent = ${space.price}`;
      priceDiv.style.fontSize = '8px';
      priceDiv.style.color = '#666';
      spaceElement.appendChild(priceDiv);
    }
    
    board.appendChild(spaceElement);
  });
}

Game Flow and Turn Management

Turn Progression System

function nextTurn() {
  gameState.currentPlayer = (gameState.currentPlayer + 1) % gameState.players.length;
  updateUI();
}

function handleLanding() {
  const currentPlayer = gameState.players[gameState.currentPlayer];
  const space = gameState.spaces[currentPlayer.position];
  
  updateUI();
  
  switch (space.type) {
    case 'property':
    case 'station':
    case 'utility':
      handleProperty(space);
      break;
    case 'tax':
      currentPlayer.money -= space.amount;
      nextTurn();
      break;
    case 'gotojail':
      goToJail();
      nextTurn();
      break;
    case 'chance':
      handleChance();
      break;
    case 'chest':
      handleCommunityChest();
      break;
    default:
      nextTurn();
  }
}

Performance and Optimization

Efficient Rendering

Memory Management

Accessibility and Usability

Visual Accessibility

User Experience

Future Enhancement Opportunities

Advanced Game Features

  1. AI Players: Computer opponents with different difficulty levels
  2. Save/Load System: Game state persistence
  3. Property Trading: Player-to-player property exchanges
  4. House/Hotel System: Property development mechanics
  5. Auction System: Bidding for declined properties

Technical Improvements

  1. Mobile Optimization: Touch-friendly responsive design
  2. Sound Effects: Audio feedback for actions and events
  3. Animation Library: Enhanced visual effects
  4. Network Multiplayer: Online gameplay capabilities
  5. Statistics Tracking: Game history and player analytics

Educational Features

  1. Tutorial Mode: Interactive game learning
  2. Mathematical Insights: Probability and strategy education
  3. Property Investment Learning: Real estate concept teaching
  4. Chase School Integration: Additional school-specific content
  5. Achievement System: Educational milestones and badges

Code Quality Assessment

Strengths

Areas for Enhancement

Conclusion

Chaseopoly represents an excellent digital implementation of the classic Monopoly board game with a creative educational theme. The sophisticated CSS Grid layout, comprehensive game mechanics, and smooth player interaction create an engaging and fully functional board game experience.

Technical Rating: 8.9/10

The application successfully demonstrates how classic board games can be effectively translated to digital formats while maintaining educational value and engaging gameplay mechanics.