Pull to refresh

Программируем Reversi на Silverlight

Silverlight *
Давно хотел начать изучать Silverlight, начинал читать литературу, пытался вникать в концепции, паттерны, но все больше как-то абстрактно, в теории. На практике, настоящую программу на Silverlight так и не попытался написать.

Но вот совсем недавно на просторах Хабра проскочила статья «программируем Reversi на Питоне». Глянув в код, ужаснулся, у меня сразу включился мотиватор. Не то, чтобы я не люблю Питон, просто я очень люблю C#.

То, что вы увидите было написано за 1 рабочий день человеком, имеющим нулевой практический опыт разработки приложений в WPF/Silverlight.

Вступление


Для тех, кто не хочет вникать как работает программа, а хочет посмотреть ее вживую, листайте статью вниз – там ссылка. Игра требует Silverlight 3, надеюсь, вы не входите в те 43% интернетовских пользователей, у которых плагин не установлен или не поддерживается (статистика взята с riastats.com).

Подготовка


Для разработки игры я использовал VS.NET 2010 RC c Silverlight Tools 4 (есть один хак, как заставить тулзы проинсталлироваться, линк легко гуглится). Сам проект написано на Silverlight 3, чтобы не напрягать тех, кто не спешит с установкой всяческих бета-версий. Т.е. VS.NET 2008 + Silverlight Tools 3 + Expression Blend 3. Все необходимое и не только можно скачать отсюда: silverlight.net/getstarted

Особенности


В игре задействованы следующие принципы Silverlight 3 и С# в частности:
  • Логика принятия решения вынесена в отдельный абстрактный класс. Идею с таблицей важности клеток перенял из питоновской реализации.
  • UI synchronization – поскольку логика игры работает в фоновом потоке, все делегаты и события должны быть правильным образом синхронизированы в UI-поток.
  • Synchronization primitives – когда игре требуется ввод пользователя, используются синхронизационные примитивы потоков – AutoResetEvent.
  • LINQ – поиск лучшего хода AI реализовано с помощью LINQ расширений.
  • Out-of-Browser – игру можно установить локально и играть без подключения к интернету.
  • Automatic Updates – при наличии подключения к интернету, игра обновляется. Размер игры в настоящее время – 14 килобайт.
  • MVVM – статус игры, счет, и т.д. привязаны к модели игры по принципу Model-View-ViewModel.
  • VisualStateManager используется для придания некой жизни интерфейсу посредством анимации фишек.
  • Localization – все сообщения игры локализованы с помощью ресурсов на русском и английском языке. Все тексты привязаны напрямую к ресурсам, т.е. если у вас русские региональные установки –интерфейс будет отображаться на русском.

Как работает игра


Есть класс Game – игра, который стартует фоновый поток, в котором будут опрашиваться ходы игроков – объектов класса GamePlayer. Это абстрактный класс, его развивают два других – GameAI – игровой модуль принятия решений и GamePC – логика приема хода от игрока.

При старте новой игры, в зависимости от выбранного пункта из комбика, конструируется новый объект игры, которому передаются различные комбинации объектов игроков GameAI и GamePC. В первый раз наблюдать битву двух AI — трансцедентально, если не сказать больше :)

GameAI принимает решения моментально, поэтому ему выставлена стандартная задержка в 100 ms. Сначала я написал игру без использования фоновых процессов, но меня стало напрягать то, что AI делает ход так быстро, что я не успевал заметить своих ходов. Когда я вставил Thread.Sleep в основной UI-поток, пострадала анимация. Я не гуру в Silverlight, гугль ответ не нагуглил, поэтому было решено унести всю логику игры в фоновый поток, а UI-поток разгрузить насовсем.

При переносе событий в фоновый поток – Silverlight начал брыкаться, ну точь в точь как WinForms. Пришлось пропустить все через глобальный UI диспетчер.

GameAI ищет оптимальный ход сначала по важности клеток, потом по выигрышу. Когда оптимальных вариантов несколько, подключается фактор случайности, т.н. душа. :) Кстати, без этой «души» белые всегда выигрывали. Теперь AI vs AI результат выигрыша 50 на 50.

Out-of-Browser приложение делается одним мышиным кликом в опциях проекта. Автоматическое обновления приложения тоже элементарно — единственный вызов в конструкторе приложения – если новая версии игры выложена на сервере, она будет скачана в локальный кэш, второй запуск приложения подхватит уже обновленную версия. Quadratisch, praktisch, gut :)

Анимация


С анимацией поначалу было не все гладко. Я хотел заставить класс Ellipse принимать различные визуальные состояния. Но оказалось, что Ellipse – не наследуется от класса Control, поэтому визуальным состоянием не подвержен. Пришлось делать свой собственный контроль Cell. Приделав этому классу двa dependency properties Stroke и Fill, которые просто управляли вложенным эллипсом, я смог заставить его выглядить как эллипс, оставаясь при этом контролем, способным анимировать себя через VisualStateManager.

В Blend-е я добавил два состояния классу Cell, Normal и Visible. В Visible добавил визуальный переход – масштаб за секунду изменяется с 0.5 до 1 используя эластичную функцию сглаживания. Получилось забавно.

Скажу так, в Blend-e я провозился больше всего, пока немного разобрался со StoryBoards, States, Transitions. Пришлось даже посмотреть пару видео-роликов из серии «Blend для чайников». Очень помогло.

Локализация


В блоге Tim Heuer нашел пост о том, как привязываться напрямую к строкам из ресурсов (resx). Полчаса пытался разобраться – почему у меня это не работает.

Оказалось, что надо читать повнимательнее. Там сказано, что это известный баг. При изменении видимости класса ресурсов – видимость конструктора не меняется, если класс public, а конструктор нет – создать такой класс через Activator в Silverlight – невозможно. Но дружище Tim уверяет, что баг будет исправлен, а пока вручную лезем в С#-класс ресурсов и меняем internal на public. Не забываем, что после добавления новых строк в ресурсы – файло будем сгенерировано по-новой, и операцию «public constructor» надо будет повторить.

Выводы


Почерпнул для себя много важного практического опыта. Получил удовольствие от баловства работы с Blend-ом (дизайнером мне, увы, стать уже не суждено). Разрабатывать оказалось достаточно удобно. Конечно, до WinForms еще далеко, но оно того стоит.

Приятно то, что много из «большого» фреймворка оказалось доступно, какие-то методы были упрощены, но в целом – все то же самое.
Стандартным браузером у меня стоит Chrome, со студией в режиме отладки он дружит плохо. Поэтому для отладки Silverlight IE8 – то, что доктор прописал.

Так же приятно поразил размер готового приложения – 14 кб.

По заявлениям MS – игра должна работать под Windows XP, Vista, 7, Mac OS X, Xbox, Windows Phone 7 Series. Посмотрим, что приподнесет MIX на следующей неделе.

Результат


Играть: тыц
Исходники: тыдыц

Код более-менее комментирован, но если есть вопросы – не стесняйтесь задавать в комментах.

И что дальше?


В планах — улучшить анимацию, сделать поле пропорционально растягивающимся на весь браузер, вшить в AI персональные игровые трики…

Ну и то, что предложишь ты, %username%

UPD: Немного разобрался с Silverlight, добавил диалог новой игры, подкрутил шаблоны фишек, выглядит значительно лучше. Порезал исходники по классам.
Tags:
Hubs:
Total votes 74: ↑46 and ↓28 +18
Views 2.6K
Comments Comments 45