نماد سایت دارک آی تی

<style>
    /* --------------------------------------------------------------------------------------------------------------------------------
     بخش CSS: استایل‌دهی شبکه و کنترل‌ها 
    -------------------------------------------------------------------------------------------------------------------------------- */
    #schelling-container {
        font-family: Tahoma, sans-serif;
        text-align: center;
        margin: 20px auto;
        padding: 20px;
        border: 1px solid #ddd;
        border-radius: 8px;
        max-width: 500px; /* برای محدود کردن عرض در صفحه */
        direction: rtl; /* برای نمایش صحیح فارسی */
    }
    h2 {
        color: #333;
        font-size: 24px;
        margin-bottom: 20px;
    }
    #schelling-grid {
        display: grid;
        /* اندازه شبکه 10x10 */
        grid-template-columns: repeat(10, 30px);
        grid-template-rows: repeat(10, 30px);
        border: 2px solid #555;
        width: 300px;
        height: 300px;
        margin: 20px auto; /* وسط‌چین کردن شبکه */
        box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.1);
    }
    .grid-cell {
        width: 30px;
        height: 30px;
        box-sizing: border-box; 
        border: 1px solid #ccc;
        text-align: center;
        line-height: 30px;
        font-weight: bold;
        font-size: 16px;
    }
    /* رنگ‌بندی عوامل (همانند نمودارهای matplotlib) */
    .agent-x {
        background-color: #1f77b4; /* آبی */
        color: white;
    }
    .agent-o {
        background-color: #d62728; /* قرمز */
        color: white;
    }
    .agent-e {
        background-color: #f0f0f0; /* خاکستری روشن (خالی) */
    }
    #controls {
        margin-bottom: 20px;
    }
    #controls button {
        padding: 10px 15px;
        font-size: 16px;
        cursor: pointer;
        background-color: #4CAF50;
        color: white;
        border: none;
        border-radius: 5px;
        transition: background-color 0.3s;
    }
    #controls button:hover {
        background-color: #45a049;
    }
    #controls label {
        font-size: 16px;
        margin: 0 10px;
    }
    #step-counter {
        margin-top: 10px;
        padding: 5px;
        font-size: 18px;
        font-weight: bold;
        color: #333;
        min-height: 25px;
    }
</style>

<div id="schelling-container">
    <h2>شبیه‌ساز تعاملی مدل شلینگ</h2>
    
    <div id="controls">
        <label for="threshold-slider">آستانه رضایت (t): </label>
        <input type="range" id="threshold-slider" min="1" max="8" value="3" oninput="updateThresholdLabel(this.value)">
        <span id="threshold-label">3</span>
        
        <button id="run-button" onclick="startSimulation()">اجرا / شروع مجدد</button>
    </div>
    
    <div id="step-counter">مرحله: 0 - شبکه آماده</div>

    <div id="schelling-grid"></div>
</div>

<script>
    // --------------------------------------------------------------------------------------------------------------------------------
    // بخش JavaScript: پیاده‌سازی منطق مدل شلینگ
    // --------------------------------------------------------------------------------------------------------------------------------

    const GRID_SIZE = 10;
    const NUM_X = 25;
    const NUM_O = 25;
    
    let grid = [];
    let threshold = 3;
    let simulationRunning = false;
    let stepCount = 0;
    
    const gridContainer = document.getElementById('schelling-grid');
    const stepCounterEl = document.getElementById('step-counter');
    const runButton = document.getElementById('run-button');
    let animationFrameId = null; // برای مدیریت حلقه requestAnimationFrame

    // --- توابع اصلی شبیه‌سازی ---

    function initializeGrid() {
        // ۱. ایجاد لیست عوامل (25X, 25O, 50E)
        let agents = [];
        for (let i = 0; i < NUM_X; i++) agents.push('X');
        for (let i = 0; i < NUM_O; i++) agents.push('O');
        for (let i = 0; i < (GRID_SIZE * GRID_SIZE - NUM_X - NUM_O); i++) agents.push('E');
        
        // ۲. بر زدن تصادفی
        for (let i = agents.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [agents[i], agents[j]] = [agents[j], agents[i]];
        }
        
        // ۳. تبدیل به شبکه 2D
        grid = [];
        for (let r = 0; r < GRID_SIZE; r++) {
            grid.push(agents.slice(r * GRID_SIZE, (r + 1) * GRID_SIZE));
        }
    }

    function isSatisfied(r, c) {
        const agentType = grid[r][c];
        if (agentType === 'E') return true; 

        let similarNeighbors = 0;
        
        // بررسی ۸ همسایه (Moore neighborhood)
        for (let dr = -1; dr <= 1; dr++) {
            for (let dc = -1; dc <= 1; dc++) {
                if (dr === 0 && dc === 0) continue; 
                
                const nr = r + dr;
                const nc = c + dc;
                
                // بررسی مرزها
                if (nr >= 0 && nr < GRID_SIZE && nc >= 0 && nc < GRID_SIZE) {
                    if (grid[nr][nc] === agentType) {
                        similarNeighbors++;
                    }
                }
            }
        }
        // شرط رضایت: تعداد هم‌نوع >= آستانه
        return similarNeighbors >= threshold;
    }

    function runSimulationStep() {
        // توقف اجرای قبلی در صورت وجود
        if (!simulationRunning) return; 
        if (animationFrameId) cancelAnimationFrame(animationFrameId);

        stepCount++;
        
        // ۱. پیدا کردن تمام عوامل ناراضی
        let unsatisfiedAgents = [];
        for (let r = 0; r < GRID_SIZE; r++) {
            for (let c = 0; c < GRID_SIZE; c++) {
                if (grid[r][c] !== 'E' && !isSatisfied(r, c)) {
                    unsatisfiedAgents.push({r, c});
                }
            }
        }
        
        // ۲. بررسی وضعیت پایداری
        if (unsatisfiedAgents.length === 0) {
            stepCounterEl.style.color = 'green';
            stepCounterEl.textContent = `✅ پایداری در مرحله ${stepCount - 1} حاصل شد.`;
            simulationRunning = false;
            runButton.textContent = "شروع مجدد";
            return;
        }

        // ۳. جابجایی تصادفی یک عامل ناراضی
        const agentToMove = unsatisfiedAgents[Math.floor(Math.random() * unsatisfiedAgents.length)];
        const agentType = grid[agentToMove.r][agentToMove.c];
        
        let emptySpots = [];
        for (let r = 0; r < GRID_SIZE; r++) {
            for (let c = 0; c < GRID_SIZE; c++) {
                if (grid[r][c] === 'E') {
                    emptySpots.push({r, c});
                }
            }
        }
        
        if (emptySpots.length === 0) {
            stepCounterEl.textContent = `❌ خطا: خانه‌ی خالی وجود ندارد.`;
            simulationRunning = false;
            return;
        }
        
        const targetSpot = emptySpots[Math.floor(Math.random() * emptySpots.length)];
        
        // جابجایی
        grid[targetSpot.r][targetSpot.c] = agentType;
        grid[agentToMove.r][agentToMove.c] = 'E';
        
        // ۴. به‌روزرسانی نمایش
        stepCounterEl.style.color = '#333';
        stepCounterEl.textContent = `مرحله: ${stepCount} - (${unsatisfiedAgents.length} ناراضی باقی‌مانده)`;
        renderGrid();
        
        // ۵. اجرای مرحله بعدی (استفاده از setTimeout برای کنترل سرعت)
        animationFrameId = setTimeout(() => requestAnimationFrame(runSimulationStep), 100); // 100ms تأخیر
    }

    // --- توابع کمکی و رابط کاربری ---

    function renderGrid() {
        gridContainer.innerHTML = '';
        
        for (let r = 0; r < GRID_SIZE; r++) {
            for (let c = 0; c < GRID_SIZE; c++) {
                const cell = document.createElement('div');
                cell.classList.add('grid-cell');
                const agentType = grid[r][c];
                
                // افزودن کلاس‌های رنگی
                if (agentType === 'X') {
                    cell.classList.add('agent-x');
                    cell.textContent = 'X';
                } else if (agentType === 'O') {
                    cell.classList.add('agent-o');
                    cell.textContent = 'O';
                } else {
                    cell.classList.add('agent-e');
                }
                
                gridContainer.appendChild(cell);
            }
        }
    }

    function updateThresholdLabel(value) {
        threshold = parseInt(value, 10);
        document.getElementById('threshold-label').textContent = value;
        // اگر در حال اجرا نبود، آستانه جدید را نمایش دهد.
        if (!simulationRunning) {
             stepCounterEl.textContent = `مرحله: 0 - آستانه جدید: ${threshold}`;
        }
    }

    function startSimulation() {
        // توقف حلقه قبلی در صورت فعال بودن
        if (animationFrameId) cancelAnimationFrame(animationFrameId);
        
        threshold = parseInt(document.getElementById('threshold-slider').value, 10);
        stepCount = 0;
        
        // شروع دوباره
        initializeGrid();
        renderGrid();
        
        simulationRunning = true;
        runButton.textContent = "در حال اجرا... (توقف خودکار)";
        stepCounterEl.style.color = '#333';
        stepCounterEl.textContent = "مرحله: 0 - در حال اجرا...";
        
        // شروع اولین گام
        animationFrameId = setTimeout(() => requestAnimationFrame(runSimulationStep), 100);
    }
    
    // اجرای اولیه برای نمایش شبکه تصادفی در بارگذاری اولیه صفحه
    initializeGrid();
    renderGrid();
</script>
خروج از نسخه موبایل