From 7aaa5646471bed02832f37a407c45d4eb49d4685 Mon Sep 17 00:00:00 2001 From: Michael Kapelko Date: Thu, 12 Sep 2019 11:55:36 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BC=D0=B5=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B2=20=D0=BA=D0=BE=D0=B4=20=D0=BA=D0=BB=D0=B0?= =?UTF-8?q?=D1=81=D1=81=D1=8B=20=D0=A4=D0=B8=D1=88=D0=BA=D0=B0=20=D0=B8=20?= =?UTF-8?q?=D0=A0=D0=B0=D1=81=D0=BA=D0=BB=D0=B0=D0=B4=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- код/2019-09-12.js | 669 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 669 insertions(+) create mode 100644 код/2019-09-12.js diff --git a/код/2019-09-12.js b/код/2019-09-12.js new file mode 100644 index 0000000..5a0ffe2 --- /dev/null +++ b/код/2019-09-12.js @@ -0,0 +1,669 @@ + +// Фишка. + +function Фишка() +{ + this.позиция = null; + this.узел = null; + this.группа = null; + + this.нейтраль = null; + this.выбор = null; +} +Фишка.prototype.показатьВыбор = function() +{ + this.узел.задатьМатериал(this.выбор); +} +Фишка.prototype.показатьНейтраль = function() +{ + this.узел.задатьМатериал(this.нейтраль); +} +Фишка.prototype.показать = function() +{ + this.узел.задатьМаску(0x0); +} +Фишка.prototype.скрыть = function() +{ + // Специальная маска OpenSceneGraph для скрытия узла от камеры. + this.узел.задатьМаску(0xFFFFFFFF); +} +Object.defineProperty(Фишка.prototype, 'имя', { + get: function() + { + return this.узел.имя; + } +}); + +// Раскладка. + +function Раскладка() +{ + this.версия = ""; + this.ширина = 0; + this.высота = 0; + this.глубина = 0; + this.позиции = []; + + // Для внутреннего пользования. + this.поля = []; +}; +Раскладка.prototype.разобратьСлужебнуюИнформацию = function(содержимое) +{ + const ключи = { + "версия": "kmahjongg-layout-v", + "комментарий": "#", + "ширина": "w", + "высота": "h", + "глубина": "d" + }; + + // ВНИМАНИЕ Версия 1.0 предполагает заданные заранее ширину и высоту. + this.версия = ""; + this.глубина = 0; + this.ширина = 32; + this.высота = 16; + this.поля = []; + + var поле = []; + var строки = содержимое.split("\n"); + for (var номер in строки) + { + var строка = строки[номер].trim(); + + // Пропуск. + if (строка.startsWith(ключи.комментарий)) + { + continue; + } + + // Служебная информация. + if (строка.startsWith(ключи.версия)) + { + this.версия = строка.split(ключи.версия)[1]; + } + else if (строка.startsWith(ключи.ширина)) + { + this.ширина = строка.split(ключи.ширина)[1]; + } + else if (строка.startsWith(ключи.высота)) + { + this.высота = строка.split(ключи.высота)[1]; + } + else if (строка.startsWith(ключи.глубина)) + { + this.глубина = строка.split(ключи.глубина)[1]; + } + // Поле. + else + { + поле.push(строка); + if (поле.length >= this.высота) + { + this.поля.push(поле.slice()); + поле = []; + } + } + } + + if (this.глубина == 0) + { + this.глубина = this.поля.length; + } +} +Раскладка.prototype.разобратьПозиции = function() +{ + this.позиции = []; + for (var номер in this.поля) + { + var поле = this.поля[номер]; + for (var строка = 0; строка < this.высота - 1; ++строка) + { + for (var столбец = 0; столбец < this.ширина - 1; ++столбец) + { + if ( + поле[строка][столбец] == "1" && + поле[строка][столбец + 1] == "2" && + поле[строка + 1][столбец] == "4" && + поле[строка + 1][столбец + 1] == "3" + ) { + this.позиции.push([номер, строка, столбец]); + } + } + } + } +} +Раскладка.prototype.разобрать = function(содержимое) +{ + this.разобратьСлужебнуюИнформацию(содержимое); + this.разобратьПозиции(); +} +Раскладка.prototype.отладка = function() +{ + var о = ""; + о += "Отладочная информация о раскладке:\n"; + о += " версия: '" + this.версия + "'\n"; + о += " ширина: '" + this.ширина + "'\n"; + о += " высота: '" + this.высота + "'\n"; + о += " глубина: '" + this.глубина + "'\n"; + о += " позиции:\n"; + for (var номер in this.позиции) + { + var п = this.позиции[номер]; + о += " п(" + номер + "): '" + п[0] + ", " + п[1] + ", " + п[2] + "'\n"; + } + return о; +} + +// Игра. + +мж.ресурсы = [ + [Б + "модели/фишка/2019-09-08.osgt", "mod"], + [Б + "текстуры/заглушка.png", "tex.stub"], + [Б + "шейдеры/освещение-изображение.vert", "ver"], + [Б + "шейдеры/освещение-изображение.frag", "fra"], + [Б + "раскладки/X_shaped.layout", "lay"], +]; + +мж.начали = new Уведомитель(); +мж.начать = function() +{ + муром.камера.позиция = [0, -70, 0]; + муром.камера.вращение = [90, 0, 0]; + мж.сцена = муром.узлы.создатьСферу("sce", 0); + мж.сцена.вращение = [60, 0, 0]; + var мир = муром.узлы.узел("mir"); + мир.добавитьДитя(мж.сцена); + + мж.начали.уведомить(); +}; + +мж.задатьНейтральныйМатериал = function() +{ + var мат = муром.материалы.создатьМатериал("N"); + var изо = муром.ресурсы.ресурс("tex.stub"); + мат.задатьТекстуру("image", изо); + var вер = муром.ресурсы.ресурс("ver").содержимое; + var фра = муром.ресурсы.ресурс("fra").содержимое; + мат.задатьШейдеры(вер, фра); + мж.сцена.задатьМатериал(мат); +}; + +мж.разобратьРаскладку = function() +{ + var содержимое = муром.ресурсы.ресурс("lay").содержимое; + мж.раскладка = new Раскладка(); + мж.раскладка.разобрать(содержимое); +}; + +мж.фишки = []; +мж.создатьФишки = function() +{ + for (var номер in мж.раскладка.позиции) + { + var ф = new Фишка(); + ф.позиция = мж.раскладка.позиции[номер]; + мж.фишки.push(ф); + } +}; + +мж.размерФишки = { + "ширина": 2.0, + "высота": 3.0, + "глубина": 1.0, +}; +мж.создатьУзлыФишек = function() +{ + const шагФишки = 2.0; + const коэффициенты = { + "x": мж.размерФишки.ширина / шагФишки, + "y": -мж.размерФишки.высота / шагФишки, + "z": мж.размерФишки.глубина, + }; + var мод = муром.ресурсы.ресурс("mod"); + + for (var номер in мж.фишки) + { + var имя = номер.toString(); + var узел = муром.узлы.создатьУзел(имя, мод); + мж.сцена.добавитьДитя(узел); + + var ф = мж.фишки[номер]; + var п = ф.позиция; + узел.позиция = [ + п[2] * коэффициенты.x, + п[1] * коэффициенты.y, + п[0] * коэффициенты.z, + ]; + ф.узел = узел; + } +}; + +мж.центрироватьСцену = function() +{ + var границы = { + "лево": 1000, + "право": -1000, + "верх": -1000, + "низ": 1000, + }; + for (var номер in мж.фишки) + { + const ф = мж.фишки[номер]; + const x = ф.узел.позиция[0]; + const y = ф.узел.позиция[1]; + if (x < границы.лево) + { + границы.лево = x; + } + if (x > границы.право) + { + границы.право = x; + } + if (y < границы.низ) + { + границы.низ = y; + } + if (y > границы.верх) + { + границы.верх = y; + } + } + + const ширина = границы.право - границы.лево + мж.размерФишки.ширина; + const высота = границы.верх - границы.низ + мж.размерФишки.высота; + мж.сцена.позиция = [-ширина / 2.0, 0, высота / 2.0]; +}; + +// http://www.rubl.com/rules/mahjong-solitaire-rules.html +мж.задатьФишкамГруппыПоследовательно = function() +{ + var группы = []; + // Генерируем группы. + const группВсего = 42; + const группПо4Дубля = 34; + for (var г = 0; г < группВсего; ++г) + { + const четыреДубля = (г < группПо4Дубля); + const колвоДублей = четыреДубля ? 4 : 1; + for (var д = 0; д < колвоДублей; ++д) + { + группы.push(г); + } + } + // Задаём. + for (var номер in мж.фишки) + { + var ф = мж.фишки[номер]; + ф.группа = Number(группы[номер]); + } +}; + +мж.применитьТемуФишек = function() +{ + const R = RR(); + + // Создать текстуры. + var обозначения = []; + var текстуры = []; + for (var номер in R) + { + var обозначение = R[номер][0]; + обозначения.push(обозначение); + var имя = "tile.tex/" + обозначение; + var содержимое = R[номер][1]; + var текстура = муром.ресурсы.создатьРесурс(имя, содержимое); + текстуры.push(текстура); + } + + // Создать материалы. + var нейтраль = []; + var выбор = []; + var вер = муром.ресурсы.ресурс("ver").содержимое; + var фра = муром.ресурсы.ресурс("fra").содержимое; + // Нейтраль. + for (var номер = 0; номер < 42; ++номер) + { + var имя = "tile.mat/" + обозначения[номер]; + var изо = текстуры[номер]; + var мат = муром.материалы.создатьМатериал(имя); + мат.задатьТекстуру("image", изо); + мат.задатьШейдеры(вер, фра); + нейтраль.push(мат); + } + // Выбор. + for (var номер = 42; номер < 83; ++номер) + { + var имя = "tile.mat/" + обозначения[номер]; + var изо = текстуры[номер]; + var мат = муром.материалы.создатьМатериал(имя); + мат.задатьТекстуру("image", изо); + мат.задатьШейдеры(вер, фра); + выбор.push(мат); + } + + // Применить тему. + for (var номер in мж.фишки) + { + var ф = мж.фишки[номер]; + ф.нейтраль = нейтраль[ф.группа]; + ф.выбор = выбор[ф.группа]; + + ф.показатьНейтраль(); + } +}; + +мж.маскаВыбора = 0x2; +мж.сделатьФишкиВыбираемыми = function() +{ + for (var номер in мж.фишки) + { + var ф = мж.фишки[номер]; + ф.узел.задатьМаску(мж.маскаВыбора); + } +} + +мж.индексПозиции = function(п) +{ + return п[0] * 1000000 + п[1] * 1000 + п[2]; +}; + +мж.индексПозиций = {}; +мж.проиндексироватьПозиции = function() +{ + for (var номер in мж.фишки) + { + var ф = мж.фишки[номер]; + var и = мж.индексПозиции(ф.позиция); + мж.индексПозиций[и] = true; + } +}; + +мж.естьСоседи = function(позиция, смещениеПоля, смещениеСтолбца) +{ + for (var смещениеРяда = -1; смещениеРяда <= 1; ++смещениеРяда) + { + var сосед = [ + Number(позиция[0]) + смещениеПоля, + Number(позиция[1]) + смещениеРяда, + Number(позиция[2]) + смещениеСтолбца, + ]; + var и = мж.индексПозиции(сосед); + if (и in мж.индексПозиций) + { + return true; + } + } + + // Соседей нет. + return false; +}; + +мж.можноВыбратьФишку = function(ф) +{ + // Проверяем наличие фишек с левой и правой сторон одновременно. + var слева = this.естьСоседи(ф.позиция, 0, -2); + var справа = this.естьСоседи(ф.позиция, 0, 2); + if (слева && справа) + { + return false; + } + + // Проверяем наличие фишек непосредственно сверху. + for (var смещение = -1; смещение <= 1; ++смещение) + { + if (this.естьСоседи(ф.позиция, 1, смещение)) + { + return false; + } + } + + // Фишка не заблокирована. + return true; +}; + +мж.номерВыбраннойФишки = null; +мж.выбраннаяФишка = null; +мж.выбранаФишка = new Уведомитель(); +мж.выбратьФишку = function() +{ + // Определить щелчок. + if (муром.мышь.нажатыеКнопки.length == 1) + { + var узел = + муром.камера.узелВПозиции( + муром.мышь.позиция, + мж.маскаВыбора + ); + if (узел) + { + var номер = Number(узел.имя); + var ф = мж.фишки[номер]; + if (мж.можноВыбратьФишку(ф)) + { + мж.номерВыбраннойФишки = номер; + мж.выбраннаяФишка = ф; + мж.выбранаФишка.уведомить(); + } + } + } +}; + +мж.отладитьВыборФишки = function() +{ + мж.выбранаФишка.подписать(function(){ + console.log( + "Выбор. Номер: '" + + мж.номерВыбраннойФишки + + "' Группа: '" + + мж.выбраннаяФишка.группа + + "'" + ); + }); +}; + +мж.показатьВыбраннуюФишку = function() +{ + мж.выбраннаяФишка.показатьВыбор(); +}; + +мж.выбранныеФишки = []; +мж.фишкиРазличаются = new Уведомитель(); +мж.фишкиСовпадают = new Уведомитель(); +мж.фишкиСравнили = new Уведомитель(); +мж.сравнитьВыбранныеФишки = function() +{ + // Собираем. + мж.выбранныеФишки.push(мж.выбраннаяФишка); + // Удостоверяемся в наличии пары фишек. + if (мж.выбранныеФишки.length != 2) + { + return; + } + var ф1 = мж.выбранныеФишки[0]; + var ф2 = мж.выбранныеФишки[1]; + // Убираем дубликат при двойном выборе одной фишки. + if (ф1.имя == ф2.имя) + { + мж.выбранныеФишки.shift(); + return; + } + // Сравниваем. + if (ф1.группа == ф2.группа) + { + мж.фишкиСовпадают.уведомить(); + } + else + { + мж.фишкиРазличаются.уведомить(); + } + мж.фишкиСравнили.уведомить(); +}; + +мж.отладитьСравнениеФишек = function() +{ + мж.фишкиСовпадают.подписать(function(){ + console.log("Фишки совпадают"); + }); + мж.фишкиРазличаются.подписать(function(){ + console.log("Фишки различаются"); + }); +}; + +мж.скрытьСовпадающиеФишки = function() +{ + мж.выбранныеФишки[0].скрыть(); + мж.выбранныеФишки[1].скрыть(); +}; + +мж.убратьСовпадающиеФишки = function() +{ + var ф1 = мж.выбранныеФишки[0]; + var ф2 = мж.выбранныеФишки[1]; + var и1 = мж.индексПозиции(ф1.позиция); + var и2 = мж.индексПозиции(ф2.позиция); + delete мж.индексПозиций[и1]; + delete мж.индексПозиций[и2]; +}; + +мж.очиститьОтображениеВыбора = function() +{ + мж.выбранныеФишки[0].показатьНейтраль(); + мж.выбранныеФишки[1].показатьНейтраль(); +}; + +мж.очиститьВыбор = function() +{ + мж.номерВыбраннойФишки = 0; + мж.выбраннаяФишка = null; + мж.выбранныеФишки = []; +}; + +мж.оставшиесяФишки = []; +мж.задатьОставшиесяФишки = function() +{ + мж.оставшиесяФишки = мж.фишки.slice(); +}; + +мж.обновилиОставшиесяФишки = new Уведомитель(); +мж.обновитьОставшиесяФишки = function() +{ + var и1 = мж.оставшиесяФишки.indexOf(мж.выбранныеФишки[0]); + мж.оставшиесяФишки.splice(и1, 1); + var и2 = мж.оставшиесяФишки.indexOf(мж.выбранныеФишки[1]); + мж.оставшиесяФишки.splice(и2, 1); + + мж.обновилиОставшиесяФишки.уведомить(); +}; + +мж.доступныеДляВыбораФишки = function() +{ + var фишки = []; + for (var номер in мж.оставшиесяФишки) + { + var ф = мж.оставшиесяФишки[номер]; + if (мж.можноВыбратьФишку(ф)) + { + фишки.push(ф); + } + } + + return фишки; +}; + +мж.естьХод = function() +{ + var фишки = мж.доступныеДляВыбораФишки(); + for (var н1 in фишки) + { + for (var н2 in фишки) + { + var ф1 = фишки[н1]; + var ф2 = фишки[н2]; + if ( + (ф1.имя != ф2.имя) && + (ф1.группа == ф2.группа) + ) { + return true; + } + } + } + + return false; +} + +мж.победа = new Уведомитель(); +мж.поражение = new Уведомитель(); +мж.проверитьЗавершение = function() +{ + var кончилисьФишки = (мж.оставшиесяФишки.length == 0); + if (кончилисьФишки) + { + мж.победа.уведомить(); + return; + } + + if (!мж.естьХод()) + { + мж.поражение.уведомить(); + } +}; + +мж.отладитьЗавершение = function() +{ + мж.победа.подписать(function(){ + console.log("Игра завершена. ПОБЕДА!"); + }); + мж.поражение.подписать(function(){ + console.log("Игра завершена. ПОРАЖЕНИЕ :("); + }); +}; + +мж.задатьОтображениеЗавершения = function() +{ + мж.победа.подписать(function(){ + муром.камера.цветОчистки = [0.2, 0.5, 0.2]; + }); + мж.поражение.подписать(function(){ + муром.камера.цветОчистки = [0.5, 0.2, 0.2]; + }); +}; + +// Игра. + +мж.начали.подписатьМного([ + мж.задатьНейтральныйМатериал, + мж.разобратьРаскладку, + мж.создатьФишки, + мж.создатьУзлыФишек, + мж.центрироватьСцену, + мж.задатьФишкамГруппыПоследовательно, + мж.применитьТемуФишек, + мж.сделатьФишкиВыбираемыми, + мж.отладитьВыборФишки, + мж.проиндексироватьПозиции, + мж.отладитьСравнениеФишек, + мж.задатьОставшиесяФишки, + мж.отладитьЗавершение, + мж.задатьОтображениеЗавершения, +]); +муром.мышь.нажатыеКнопкиИзменились.подписать(мж.выбратьФишку); +мж.выбранаФишка.подписатьМного([ + мж.показатьВыбраннуюФишку, + мж.сравнитьВыбранныеФишки, +]); +мж.фишкиСовпадают.подписатьМного([ + мж.убратьСовпадающиеФишки, + мж.обновитьОставшиесяФишки, + function(){ + setTimeout(мж.скрытьСовпадающиеФишки, 200); + }, +]); +мж.фишкиРазличаются.подписать(function(){ + setTimeout(мж.очиститьОтображениеВыбора, 200); +}); +мж.фишкиСравнили.подписать(function(){ + setTimeout(мж.очиститьВыбор, 200); +}); +мж.обновилиОставшиесяФишки.подписать(мж.проверитьЗавершение); +// Начать после загрузки ресурсов. +муром.ресурсы.получить(мж.ресурсы, мж.начать);