Game of Life

Предадени решения

Краен срок:
26.01.2016 15:00
Точки:
10

Срокът за предаване на решения е отминал

Задачата ви на това домашно ще бъде да имплементирате Conway’s Game of Life с HTTP интерфейс. Правилата на играта са прости и целта ви ще е да създадете сървър, отговарящ за безкрайния двуизмерен свят на играта и имплементиращ въпросните правила. Комуникацията с вашия сървър ще става по HTTP.

Правила

Светът, в който се развива действието, е безкрайна двуизмерна координатна система. Ще го наричаме игрална дъска. На всяка координата съответства клетка. Всяка клетка има точно две състояния — жива или мъртва. Освен състояния, клетката има и осем съседа — това са обграждащите я клетки (лява, дясна, горна, долна, горе-ляво, горе-дясно, долу-ляво и долу-дясно). Поколение ще наричаме моментното състояние на игралната дъска — т.е. координатите на живите в момента клетки. От текущото поколение можем да поискаме да еволюира в следващо, прилагайки следните правила:

  • Всяка жива клетка с по-малко от два живи съседа, умира (underpopulation).
  • Всяка жива клетка с два или три живи съседа продължава да живее и в следващото поколение.
  • Всяка жива клетка с повече от три живи съседа, умира (overpopulation).
  • Всяка мъртва клетка с точно три живи съседа, се ражда (reproduction).

Докато "генерирате" следващото поколение, текущото не се променя и правилата се прилагат само над текущото.

Обикновено играта започва с някакво ръчно зададено, първоначално състояние. За координати ще използваме цели числа.

Реализация

Когато се говори за координати в условието, то се следват нормалните конвенции за х и y координати. Когато става въпрос за двойка и не са споменати изрично, то първото число е x, второто - y.

За да имплементирате гореописаните правила, ще искаме от вас да създадете тип GameOfLifeHandler, който да отговаря на http.Handler интерфейса, както и функцията

func NewGameOfLifeHandler([][2]int64) *GameOfLifeHandler

NewGameOfLifeHandler

За да можем да зададем началното състояние на игралната дъска, е необходимо да можем да конструираме инстанции на типа GameOfLifeHandler, подавайки на NewGameOfLifeHandler нула или повече двойки координати на живите клетки.

Пример:

gofh := NewGameOfLifeHandler([][2]int64{
    {2, 3},
    {2, 4},
    {-20, 4},
    {-42, 0},
})

GameOfLifeHandler

Вашия handler трябва да отговаря на няколко вида заявки, които ще дават на клиентите информация за състоянието на игралната дъска, както и възможност да я променят. Заявките се идентифицират чрез техния път и метод.

GET заявките от клиенти ще имат аргументи в query-то, а всички останали като JSON в тялото на request-a.

Следва списък с всички заявки, на които трябва да отговаря вашия сървър. Свободни сте да си добавите такива извън условието.

Статус на клетка

GET /cell/status/?x=&y=

x и y ще са цели числа, задаващи координати на клетка. Сървъра ви трябва да върне JSON обект който има един ключ 'alive'. Стойноста на този ключ е истина ако клетката е жива. Неистина в противен случай.

За заявка

GET /cell/status/?x=2&y=3 HTTP/1.1

може да се получи отговора

{
    "alive": true
}

Списък с всички живи клетки

GET /generation/ HTTP/1.1

Като отговор на тази заявка трябва да върнете json обект, който има два ключа: 'generation' и 'living'. Първия е поредния номер на тази популация, а втори е списък от "двойки" координати, представляващи всички живи клетки. Пример:

{
    "generation": 5,
    "living": [[42, 32], [1, -3], [0, 5]]
}

Тъй като в JSON няма понятие "n-торка", то "двойка" е просто array с два елемента.

Добавяне на живи клетки

POST /cells/ HTTP/1.1

[{"x": 42, "y": 43}, {"x": -5, "y": 10}]

С такива заявка клиент може да добави живи клетки на дъската. Ако някоя клетката на зададените координати не е жива, то тя трябва да стане таква след тази заявка. Ако е вече жива - не трябва да се случи нищо с нея.

Тялото на заявката съдържа списък (JSON Array) с обекти, имащи два ключа: x и y.

Сървъра трябва да отговори с HTTP статус 201 на такава заявка.

Пресмятане и преминаване на следващото поколение

POST /generation/evolve/ HTTP/1.1

С тази заявка клиент казва на вашия сървър да изчисли следващото поколение. Когато получите такава заявка трябва да приложите правилата върху текущото състояние на дъската. Не забравяйте да увеличите поредния номер на следващата популация.

Изчистване на дъската

POST /reset/ HTTP/1.1

С тази заявка игралната дъска се изчиства изцяло. На нея не трябва да остане нито една жива клетка. Номера на поколението трябва да се върне на 0.

Допълнителни пояснения

  • В случай, че не е споменато изрично, очаква се да се отговаря с HTTP статуса 200 всички заявки с тяло. 204 за всички които нямат тяло.
  • За клиентски заявки, които не отговарят на описаните тук правила трябва да върнете статус 400 и съобщение какво не е било наред.
  • Освен ако не са със сгрешен метод, за което има 405 или път, който изобщо не съществува - 404. За всичко, което не е споменато изрично, очакваме логично поведение в рамките на HTTP RFC-то.
  • Сървъра ви не трябва да "движи" играта напред самостоятелно. Пресмятането на новo поколение става само чрез клиентска заявка на /generation/evolve/.
  • За методи, където не е изрично споменато, Handler-a а ви трябва да отговаря с празно тяло.
  • Генерирането на ново поколение не трябва да "блокира" GET заявки. Двете неща трябва да се случват конкурентно. Всички заявки променящи дъската (POST), обаче, трябва да изчакат докато това се случи.
  • Вашия handler трябва да подържа HTTP/1.0 и HTTP/1.1 заявки. Ако използвате стандартната библиотека това е така по подразбиране.
  • Поредния номер на първото поколение трябва да 0. Това е поколението, което е било създадено единствено чрез попълване (NewGameOfLifeHandler и/или POST /cells/), а не чрез HTTP метода за пресмятане на следващо поколение.
  • Координатите на дъската ще се събират в int64. Стриктно погледнато, така тя не е наистина безкрайна.