IT Blog
RSS icon Email icon
  • Big Bombs

    Posted on March 3rd, 2013 Krasi Nikolov No comments

    Задачата за третия етап от конкурса на PC Magazin и Telerik, беше много интересна. За първи път имаше доста програмиране в приложната част. Правилата на играта, която трябваше да се имплементира, са сравнително прости.
    Имаме поле 101х101, защитникът разполага със солни мини, които трябва да се охраняват. Самата защита се осъществява от оборудвани пилета. Нападателят разполага с две бойни единици: Голяма бомба, която има радиус от 0 до 19, и гуци с гърмелка, което има радиус 1.5.
    Нападателят и защитникът разполагат с по 200 пари, за да разположат своите бойни единици.
    Цените на единиците са:
    Мина – 6 пари
    Пиле – 1 пара
    Гуци – 7 пари
    Голяма бомба – от 10 до 200 пари в зависимост от радиуса.
    Играта се осъществява последователно т.е. първо защитника реди своята защита, а след това нападателя атакува.
    В приложната част трябваше да покажем играта в три варианта:
    1. Игра в защита – редим защита и програмата ни атакува.
    2. Игра в атака – програмата ни генерира защита и ние я атакуваме.
    3. Игра м/у двама играчи – последователно се реди защита и атака.
    Освен това е необходимо да се направи зала на славата.
    За направата на приложението използвах PHP като сървърна част и JavaScript(jQuery). Изборyt ми беше предопределен от това, че засега нямам уменията да направя всичко това в .NET среда.
    В началото задачата ми изглеждаше сравнително проста, но постепенно осъзнах, че има доста работа за вършене.
    Като начало направих базата, върху която да се надгради останалото. Бойното поле представлява една таблица със 101 реда и 101 колони, която генерирах сравнително лесно на PHP.

    </pre>
    = 0; $y--) {
     echo ''; for ($x = 0; $x <= 100; $x++) { echo ' '; } echo ' '; } ?>
    <table><!--?php     for ($y = 100; $y -->
    <tbody>
    <tr>
    <td id="' . $x . '_' . $y . '" title="x=' . $x . ' y=' . $y . '" onclick="PutObject(' . $x . ',' . $y . ')"></td>
    </tr>
    </tbody>
    </table>
    <pre>
    

    След това се насочих към следващия важен елемент – страничната лента. Разположих бутоните, с които ще се играе – мини, пилета, брой и пари за защитник; бомба, гуци, брой(радиус) и пари за нападател. Трябваше да се даде възможност да се избере елемент, да можеш да определиш бройката или радиуса и да поставиш съответния елемент върху полето. Цялата тази функционалност я направих с JavaScript/jQuery.

    function PutObject(x,y){
        var count = parseInt($("#count span").html());
        var money = parseInt($('#money span').html());
        var type = $('aside li.selected').attr('id');
        var isDefense = $('#'+x+'_'+y).hasClass('defense');
        var isAttack = $('#'+x+'_'+y).hasClass('attack');
        var price = 0;
        if (((type == 'pigs' || type=='bomb') && isAttack) ||
            ((type == 'chickens' || type=='mine') && isDefense)){
            alert('Field allready taken!')
        } else {
            switch (type) {
                case 'mine':
                    price = 6;
                    break;
                case 'chickens':
                    price = count;
                    break;
                case 'bomb':
                    price = 10+10*count;
                    break;
                case 'pigs':
                    price = 7*count;
                    break;
    
                default:
                    break;
            }
    
            if (money >= price) {
                switch (type) {
                    case 'chickens':
                        $('#'+x+'_'+y).addClass('defense');
                        $('#'+x+'_'+y).html('<img class="defense" alt="" src="images/chicken.png" width="29" height="29" />');
                        $('#money span').html(money - count);
                        $('#defense p').append('chickens' + ' ' + count + ' '+ x + ' ' + y + '
    ');
                        $('#'+x+'_'+y).append('<span>'+count+'</span>');
                        break;
                    case 'mine':
                        $('#'+x+'_'+y).addClass('defense');
                        $('#'+x+'_'+y).html('<img class="defense" alt="" src="images/mine.png" width="29" height="29" />');
                        $('#money span').html(money - 6);
                        $('#defense p').append('mine' + ' ' + x + ' ' + y + '
    ');
                        $('#'+x+'_'+y).append('<span>'+count+'</span>');
                        break;
                    case 'pigs':
                        $('#'+x+'_'+y+' span').remove();
                        $('#'+x+'_'+y).addClass('attack');
                        $('#'+x+'_'+y).append('<img class="attack" alt="" src="images/pig.png" width="29" height="29" />');
                        $('#money span').html(money - count*7);
                        $('#attack p').append('pigs' + ' ' + count + ' '+ x + ' ' + y + '
    ');
                        $('#'+x+'_'+y).append('<span>'+count+'</span>');
                        break;
                    case 'bomb':
                        $('#'+x+'_'+y+' span').remove();
                        $('#'+x+'_'+y).addClass('attack');
                        $('#'+x+'_'+y).append('<img class="attack" alt="" src="images/bomb.png" width="29" height="29" />');
                        $('#money span').html(money - 10 - 10*count);
                        $('#attack p').append('bomb' + ' ' + count + ' ' + x + ' ' + y + '
    ');
                        $('#'+x+'_'+y).append('<span>'+count+'</span>');
                        $('li').removeClass('selected');
                        $('#pigs').addClass('selected');
                        $('#count').html('Count:<span>1</span>');
                        $('aside').addClass('bombed');
                        break;
                    default:
                        alert('Please choose element');
                        break;
                }
            } else {
                alert('You don\'t have enough money!')
            }
        }
    }
    

    Това е функцията, която поставя обект в клетка от таблицата и намалява съответното количество пари. Следваща стъпка беше да направя това, което беше по лесно, а именно защитата. Направих простичка функциика на PHP, която генерира защитно поле и скрипт на JavaScript, за да разположа генерираните елементи в таблицата. Логиката на защитата е сравнително проста. Деля защита на случаен брой групи и ги разпределям на случайни позиции по полето. Всяка група съдържа по една мина и няколко пилета за защита.
    Направих една jQuery заявка, която вика генератора, който е на сървъра. След като се генерира поле на сървъра чрез callback функция, разполагам елементите на полето.
    Това е JavaScript-a:

    function GenerateDefense(){
        ClearField();
        $('.commands').empty();
        $('aside li.selected').removeClass();
        $('#count').html('Count<span>1</span>');
        $('#money').html('Money<span>200</span>');
        $('aside').removeClass();
        $.getJSON("generate.php?",
            function(data){
                if (data) {
                    $.each(data, function(key,value) {
                        $('#'+value[1]+'_'+value[2]).addClass('defense');
                        if (value[3] == 'chicken') {
                            $('#'+value[1]+'_'+value[2]).html('<img alt="" src="images/chicken.png" width="29" height="29" />');
                            $('#defense p').append('chickens' + ' ' + value[0] + ' '+ value[1] + ' ' + value[2] + '
    ');
                        }else {
                            $('#'+value[1]+'_'+value[2]).html('<img alt="" src="images/mine.png" width="29" height="29" />');
                            $('#defense p').append('mine' + ' '+ value[1] + ' ' + value[2] + '
    ');
                        }
                        $('#'+value[1]+'_'+value[2]).append('<span>'+value[0].toString()+'</span>');
                    });
                }
            });
    }
    

    И PHP генератора:

    session_start();
    $money = 200;
    $mines = rand(10, 20);
    $money -= $mines * 6;
    $chickenInGroup = floor($money / $mines);
    for ($i = 1; $i <= $mines; $i++) {     // mine position     do {         $mineX = rand(1, 99);         $mineY = rand(1, 99);     } while ($taken[$mineX][$mineY] == true);     $taken[$mineX][$mineY] = true;     $list[] = array(1, $mineX, $mineY,'mine');     do {         $add = rand(-1, 1);         $chickenX = $mineX + $add;         $add = rand(-1, 1);         $chickenY = $mineY + $add;     } while ($taken[$chickenX][$chickenY] == true);     $taken[$chickenX][$chickenY] = true;     $list[] = array($chickenInGroup, $chickenX, $chickenY, 'chicken');     $money -= ($chickenInGroup); } while ($money > 0){
        $chickenX = rand(1, 99);
        $chickenY = rand(1, 99);
        if ($taken[$chickenX][$chickenY] != true) {
            $list[] = array(1, $chickenX, $chickenY, 'chicken');
            $money--;
        }
    }
    $_SESSION['defense'] = $list;
    echo json_encode($list);
    

    Трябваше да се добавят няколко прости функции, за да може да се сменят картинките в полето при проиграване на битката, и с това защитата бе готова. Забравих да спомена, че в дясно разположих две полета, в които записвам съответно ходовете за атака и защита. Освен това, всички изчисления се осъществяват на сървъра т.е. дори някой да манипулира нещата в браузъра, изчисленията би трябвало да са коректни.
    Следваше направата на атаката, която на практика е един вид повторение на алгоритмичната част. Моят алгоритъм е сравнително прост, тъй като в противен случай потребителите биха чакали сравнително дълго. Все пак всички изчисления се правят на сървъра, а хостинга ми има ограничение за ресурсите на процесора. Логиката за атаката е следната – проверявам всички варианти за бомба. Първоначално търсех максимална ефективност, но след това реших че е по- добре да направя лека корекция, за да дам предимство на по- големите бомби. По отношение на атаката с гуцита, просто пращам атака на първото място, където тя е възможна. Алгоритъмът може да работи и като търси пак най-добрата ефективност, но така рискувам потребителя да не ме чака.
    Ето и PHP кода, който генерира позиция за бомба. При гуцитата нещата са сходни.

            // BOMB calculations
            $damages = 0;
            $money = 200;
            $bestResult = 0;
            $defense = $_SESSION['defense'];
            // Search bomb position
            for ($x = 0; $x <= 100; $x++) {
                for ($y = 0; $y <= 100; $y++) {
                    for ($radius = 0; $radius < 20; $radius++) {                     $result = Calculations::DamagesInRadius($x, $y, $radius, $defense);                     $coef = $result / (10 + $radius * 2);                     if ($coef > $bestResult) {
                            $topResult = $result;
                            $bestResult = $coef;
                            unset($bestBomb);
                            $bestBomb = array('bomb', $radius, $x, $y);
                        }
                    }
                }
            }
            //echo $topResult;
            $damages += $topResult;
            $money -= (10 + 10 * $bestBomb['1']);
            $hitted[] = Calculations::DestroyInRadius($bestBomb['2'], $bestBomb['3'], $bestBomb['1'], $defense);
            $bestList[] = $bestBomb;
    

    Следващата стъпка беше игра за двама, което е сравнително просто, използвах готовите елементи от защитата и атаката.
    За последната част със залата на славата използвах MYSQL база данни. Реших да направя просто решение и да пазя най- добрите резултати в две таблици – за защита и атака. В залата на славата показвам най- добрите 15 резултата. Добавих функционалност за записване на най-добрите резултати в залата на славата. В защита записвам само резултатите на хора, които са похарчили всичките си пари. Това направих пак с AJAX заявки към PHP файл на сървъра:

    function DefenseToFame(){
        $.getJSON('inc/defense_to_fame.php', {
            'name':$('#fame_defense').val()
        }, function(){
            ShowMessage('Your result was added in Hall of Fame');
        });
    }
    

     

    include '../inc/db.php';
    if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
        $name = trim($_REQUEST['name']);
        $stmt = $db->prepare('INSERT INTO `defense`(`name`, `result`) VALUES (:name,:result)');
        $stmt->bindParam(':name',$name);
        $stmt->bindParam(':result',$_SESSION['bestDefense']);
        $added = $stmt->execute();
        echo $added;
    }
    

    С това приложението ми беше готово. Не остана много време за дизайна, но пък имаме цялостно работещо приложение.
    Това е пълния код на приложението:
    Github Repository

    Leave a reply