Рассылка закрыта
При закрытии подписчики были переданы в рассылку "RFpro.ru: Flash-анимация" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Программирование игр на Flash/Flex Логическая игра Ним (Окончание)
Здравствуйте, уважаемые читатели! Сегодня мы рассмотрим алгоритм игры Ним и способ реализации этого алгоритма на языке ActionScript. В прошлый раз мы разработали интерфейс, построили два модуля, и связали их. В этой части данного упражнения посмотрим реализацию логики игры За это время я несколько изменил интерфейс в сторону упрощения. Большое удобство Flex, в частности, в том, что можно просто скопировать текст Главного модуля через буфер обмена, и в режиме редактирования исходного модуля (Source) вставить эти строки. Потом можно перейти в режим дизайна и посмотреть, что получилось. Давайте воспользуемся этой возможностью. Вот текст Главного модуля нашей программы: <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="400" height="220" cornerRadius="6" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#FEFCFC, #FEFCFC]"> <mx:NumericStepper x="339" y="22" id="nstones1" value="3" minimum="0" maximum="6"/> <mx:NumericStepper x="339" y="126" id="nstones3" value="5" minimum="0" maximum="6"/> <mx:NumericStepper x="339" y="75" id="nstones2" value="4" minimum="0" maximum="6"/> <mx:Canvas x="10" y="22" width="310" height="45" id="canvas1"> </mx:Canvas> <mx:Canvas x="10" y="75" width="310" height="45" id="canvas2"> </mx:Canvas> <mx:Canvas x="10" y="126" width="310" height="45" id="canvas3"> </mx:Canvas> <mx:Button x="10" y="190" label="Загрузка..." width="244" enabled="true" id="begBtn" click="begClick()"/> <mx:Script><![CDATA[ import Stones; // Связь данного файла с файлом класса private var _st:Stones = new Stones(this); // Вызов конструктора класса и передача экземпляру класса указателя на самого себя (this) public var rules:XML=<rules/> private function begClick():void{ _st.logic(); } ]]></mx:Script> <mx:CheckBox x="262" y="188" label="Ход компьютера" width="128" id="nextMove" selected="true"/> </mx:Application> Скопировали, посмотрели, увидели интерфейс, представленный на рисунке. Как видите, имеются три элемента NumericStepper, с которыми мы знакомились в прошлый раз, и три элемента класса Canvas, на которые мы будем помещать камни.Кнопка и флажок пояснений не требуют. Алгоритм игры Математически доказано следующее. Пусть количество камней в каждой кучке n1, n2 и n3 соответственно. Если сумма по модулю 2 этих трех чисел равна 0, то такая позиция безопасна, в противном случае она опасна. Из опасной позиции при своем ходе можно получить безопасную, а из безопасной - только опасную. Следовательно, стратегия игры состоит в том, чтобы получить при своем ходе безопасную позицию, если это возможно. 1. Напишем функцию, которая возвращает сумму по модулю 2 трех чисел, передаваемых ей в качестве аргументов: private function knimSum(n1:uint,n2:uint,n3:uint):uint{ return n1 ^ n2 ^ n3; } Как видите, совсем просто. 2. Взаимодействие игрока и компьютера будем описывать в терминах переходов игры из одного состояния в другое. Для этого создадим в нашем классе свойство State и переменную private var _state:int = -1; У меня немного изменился план игры по ходу его реализации. Первоначально планировалось использовать значения этого свойства от 0 до 3. Затем я счел нужным добавить дополнительное значение, которое соответствует начальному состоянию, когда пользователь может просмотреть правила игры, и способ работы с программой. Чтобы не менять остальные, уже назначенные значения, пришлось назначить начальное состояние свойства _state = -1. При переходе программы от одного свойства к другому изменяется текст, отображаемый на кнопке, и, что самое главное, реакция программы на нажатие кнопки (обработка события) также зависит от текущего состояния. На своем сайте http://master-teacher.ru я помещу специальный небольшой материал, посвященный свойствам классов и двум методом, называемым Геттер и Сеттер. В тексте модуля Вы найдете метод сеттер, то есть, метода класса, вызваемого тогда, когда изменяется свойство State. private function set State(newState:uint):void{ 3. Каждый раз, когда пользователь нажмет на кнопку, будет вызван метод logic: public function logic():void{ // Вызов при нажатии на кнопку 4. В этом методе используются значения свойств: _currentCanvas, и _currentNs, используемые в функции logic. Когда игрок изменяет значение количества камней, выбираемых в одной из кучек, требуется запомнить, какая кучка и какой элемент NumericStepper используются. Эта информация и хранится в данных свойствах: private var _currentCanvas:Canvas; private var _currentNs:NumericStepper; Значения этих свойств изменяются в обработчике события (см. текст модуля: private function nstChange(e:Event):void{ //Если пользователь изменил значение одного элемента Связь этого обработчика с соответствующими событиями описывается так: nstones1.addEventListener("change",nstChange); nstones2.addEventListener("change",nstChange); nstones3.addEventListener("change",nstChange); 5. Осталось рассмотреть, как компьютер находит и выполняет очередной ход. Алгоритм несложен: перебирая все возможные ходы, пытаемся найти ход, приводящий к безопасной позиции. Если это удается, то перебор завершается, и делается выбранный ход. Если это не удается (позиция опасна и не удается превратить ее в безопасную), делается случайный ход. Алгоритм реализован в функции: private function determMove():void 6. Как я уже отмечал, в окончательном варианте игры добавлен небольшой справочник по правилам игры, использованию программы и возможным результатам. Этот справочник я оформил в виде XML-файла, который читается отдельным модулем GetKbFromFile. Использование языка XML в программировании игр заслуживает отдельного рассмотрения, этому будет посвящен отдельный выпуск (статья). Здесь ограничимся лишь текстом данного модуля: package { import flash.events.*; import flash.net.*; import flash.utils.*; public class GetKbFromFile{ private var __parent:Stones; public function GetKbFromFile(pa:Stones) { __parent = pa } public function ask(what:String):void { var loader:URLLoader=new URLLoader(); loader.dataFormat = URLLoaderDataFormat.TEXT; loader.addEventListener(Event.COMPLETE, handleComplete); loader.load( new URLRequest(what)); } private function handleComplete( event:Event ):void { __parent.answer(event.target.data); } } } А вот и полный текст модуля Stones на ActionScript: // Класс Stones для игры Ним package{ import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import mx.containers.Canvas; import mx.controls.Button; import mx.controls.CheckBox; import mx.controls.Image; import mx.controls.NumericStepper; import mx.controls.TextArea; public class Stones extends Sprite{ //Имя класса должно совпадать с именем файла. private var __parent:Knim; //Это свойство будет использоваться для связи с главным модулем private var _state:int = -1; private var _list1:Array = new Array(); private var _list2:Array = new Array(); private var _list3:Array = new Array(); private var _panel:Canvas = new Canvas; private var _area:TextArea = new TextArea; private var _currentCanvas:Canvas; private var _currentNs:NumericStepper; private var _getKb:GetKbFromFile; private var _texts:Array = new Array(); //тексты из файла помощи public function Stones(pa:Object){ __parent = pa as Knim; _getKb = new GetKbFromFile(this); _getKb.ask("stoneRules.xml"); //чтение файла помощи с сервера. Формат файла XML } public function answer(txt:String):void{ //Вызывается объектом _getKb при завершении загрузки файла. with(__parent){ rules = XML(txt); var str:String = rules.toXMLString(); begBtn.enabled = true; } State =0; } private function crArea():void{ //Вызов при State = 0 with (__parent){ _panel.height = begBtn.y + begBtn.height; _panel.width = _area.width = stage.width; _area.height = 0.7 * _panel.height; _panel.addChild(_area); var xx:uint = 5; var yy:uint = _area.height-45; var npart:uint =0; for each(var element:XML in rules.elements()){ //чтение файла помощи и создание кнопок для его демонстрации _texts.push(element.text()); var txt:String = element.@txt; var bt:Button = new Button(); bt.width = 95; bt.label = txt; bt.x=xx; bt.y = _area.y+ _area.height; bt.name = "b"+npart.toString(); bt.addEventListener(MouseEvent.CLICK,btclick); xx += bt.width + 2; _panel.addChild(bt); npart++; } addChild(_panel); } State = 1; } private function crNewGame():void{//Вызов при State = 1 with (__parent){ nstones1.addEventListener("change",nstChange); nstones2.addEventListener("change",nstChange); nstones3.addEventListener("change",nstChange); crStones(nstones1.value,45,canvas1); crStones(nstones2.value,45,canvas2); crStones(nstones3.value,45,canvas3); _list1 = canvas1.getChildren(); _list2 = canvas2.getChildren(); _list3 = canvas3.getChildren(); _currentCanvas = canvas1; _currentNs = nstones1; nextMove.addEventListener(Event.CHANGE,nextMoveChange); } State = 2; } private function nextMoveChange(e:Event):void{ with (e.target as CheckBox) { if (selected){ State = 2; }else{ State = 3; } } } private function btclick(e:MouseEvent):void{ //Демонстрация текста из файла помощи var na:uint = Number(e.target.name.charAt(1)); var txt:String = _texts[na]; if(txt) _area.text = txt; else __parent.removeChild(_panel); } private function nstChange(e:Event):void{ //Если пользователь изменил значение одного элемента // NumericStepper, остальные обнуляются. //поэтому не удастся за один ход снять камни из разных наборов with (__parent){ switch (e.target){ case(nstones1): nstones2.value = nstones3.value = 0; _currentCanvas = canvas1; _currentNs = nstones1; break; case(nstones2): nstones1.value = nstones3.value = 0; _currentCanvas = canvas2; _currentNs = nstones2; break; case(nstones3): nstones1.value = nstones2.value = 0; _currentCanvas = canvas3; _currentNs = nstones3; break; } } } private function crStones(nSt:uint,dx:uint,canvas:Canvas):void{ var cx:uint = 0; for (var i:uint = 0; i<nSt;i++){ var img:Image = new Image(); img.source = "almaz.jpg"; img.x = cx; cx += dx; img.y = 0; canvas.addChild(img); } } private function delStones(canvas:Canvas,nstones:NumericStepper):void{ if(canvas.numChildren >= nstones.value){ while ( nstones.value > 0) { canvas.removeChildAt(0); nstones.value--; } } if (gameFinished()) resGame("Я проиграл."); else State = 2; } private function restore():void{ for each (var im:Image in _list1) __parent.canvas1.addChild(im); for each (im in _list2) __parent.canvas2.addChild(im); for each (im in _list3) __parent.canvas3.addChild(im); if(__parent.nextMove.selected) State = 2; else State = 3; } // Логика игры public function logic():void{ // Вызов при нажатии на кнопку switch(_state){ case(0): crArea(); break;//Окно с правилами игры и с информацией о ее завершении. case(1): crNewGame(); break; //Начальное состояние case(2): determMove(); break; case(3): delStones(_currentCanvas,_currentNs); break; case(4): restore(); } } private function set State(newState:uint):void{ switch(newState){ case(0): var txt:String = "Правила игры";break; case(1): txt = "Новая игра";break; case(2): txt = "Ход компьютера"; __parent.nextMove.selected = true; break; case(3): txt = "Ход игрока"; __parent.nextMove.selected = false; break; case(4): txt = "Еще раз";break; } with (__parent){ begBtn.label = txt; if ((newState == 2)||(newState == 3)){ nextMove.visible = true; }else{ nextMove.visible = false; } } _state = newState; } private function gameFinished():Boolean{ with(__parent) return ((canvas1.numChildren + canvas2.numChildren + canvas3.numChildren) == 0); } private function knimSum(n1:uint,n2:uint,n3:uint):uint{ return n1 ^ n2 ^ n3; } private function resGame(str:String):void{ _area.text = str; __parent.addChild(_panel); State = 4; } private function determMove():void{ with (__parent){ var n1:uint = canvas1.numChildren; var n2:uint = canvas2.numChildren; var n3:uint = canvas3.numChildren; var n:uint = 1; while ((n <= n1) && knimSum(n1-n,n2,n3)) n++; if(n<=n1){ //Найден результат nstones1.value = n; delStones(canvas1,nstones1); }else{ n = 1; while ((n <= n2) && knimSum(n1,n2-n,n3)) n++; if(n <= n2){ nstones2.value = n; delStones(canvas2,nstones2); }else{ n = 1; while ((n <= n3) && knimSum(n1,n2,n3-n)) n++; if(n <= n3){ nstones3.value = n; delStones(canvas3,nstones3); }else{ // Позиция опасна, и не удается найти ход if (canvas1.numChildren){ var canvas:Canvas = canvas1; var nStepper:NumericStepper = nstones1; nStepper.value }else{ if (canvas2.numChildren){ canvas =canvas2; nStepper = nstones2; }else{ canvas =canvas3; nStepper = nstones3; } } n = canvas.numChildren; n *= int(Math.random() + 1 ); nStepper.value = n; delStones(canvas,nStepper); } } } if (gameFinished()) resGame("Я выиграл!"); else State = 3; } } } } |
В избранное | ||