- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
Procedure TForm1.CreateObjects(Column, Row: Integer); // Процедура создания кнопок (ячеек).
Var
i, j, iLeft, iTop, iRow : Integer; // Переменные, отвечающие за расположение ячеек на форме. (Кроме iRow, она содержит номер ячейки по горизонтали.)
SpeedButton : array of TSpeedButton; // Собственно, сам массив с ячейками.
cColumn : Char; // Номер ячейки по вертикали.
iMine : ShortInt; // Дополнительная переменная кол-ва бомб на поле.
BEGIN
{Присваиваем начальные значения переменным. :7:}
iLeft := 0; // Начальная позиция по горизонтали.
iTop := 5; // Начальная позиция по вертикали.
iRow := 1; // Номер ячейки по горизонтали.
iMine := MineCount; // Кол-во мин на поле.
cColumn := 'A'; // Номер ячейки по вертикали.
SetLength(SpeedButton, Row+1); // Массив с ячейками.
{------------------------------------------ :7:}
{Создаём новые списки. :8:}
FObjectList := TObjectList.Create; // Список с объектами.
Form2.FMineList := TStringList.Create(); // Список с номерами мин на поле.
{--------------------- :8:}
{Главный цикл создания игрового поля :9:}
For i := 1 to Column do // Запускаем цикл создания по вертикали.
Begin
For j:=1 to Row do // Теперь тоже самое по горизонтали.
bEgin
SpeedButton[j] := TSpeedButton.Create(Self); // Создаём очередную ячейку на поле.
SpeedButton[j].Parent := GamePanel; // Этим параметром указываем, что ячейка принадлежит первой форме.
SpeedButton[j].Top := iTop; // Задаём расположение ячейки по вертикали.
SpeedButton[j].Name := cColumn + IntToStr(iRow); // Задаём имя ячейки.
SpeedButton[j].Flat := True; // Используем св-во кнопок не отображаться на рисунке.
SpeedButton[j].Font.Color := clBlue; // Делаем цвет текста синим.
SpeedButton[j].Font.Style := [fsBold]; // Делаем шрифт жирным.
SpeedButton[j].OnClick := SpeedButton1.OnClick; // Указываем, какая процедура будет вызываться при клике на ячейку.
SpeedButton[j].OnMouseDown := SpeedButton1.OnMouseDown; // Указываем, какая процедура будет вызываться при нажатии на ячейку.
SpeedButton[j].Left := iLeft+8; // Задаём расположение ячейки по горизонтали, смещая её на 8 пикселей вправо.
{Устанавливаем бомбу в ячейку. :10:}
If (iMine <> 0) and (SpeedButton[j].Name <> 'A1') then // Если кол-во бомб не превышено, то...
beGin
Randomize; // Генерируем список ПСЧ.
SpeedButton[j].Tag := Random(2); // Случайным образом задаём, будет ли бомба находится в ячейке.
If SpeedButton[j].Tag = 1 then
begin
Form2.FMineList.Add(SpeedButton[j].Name); // Добавляем новую запись в массив бомб.
Dec(iMine); // Уменьшаем счетчик мин на единицу.
end;
enD;
{----------------------------- :10:}
FObjectList.Add(SpeedButton[j]);// Добавляем новую запись в массив ячеек.
iLeft := iLeft + 24; //Увеличиваем расстояние между ячейками.
If iRow = Row then iRow := 1 // Если номер следующей ячейки по горизонтали неправильный, то меняем его на 1.
Else Inc(iRow); // Иначе увеличиваем номер.
eNd;
cColumn := Char(Ord(cColumn) + 1); // Увеличиваем номер ячейки по вертикали.
iTop := iTop + 24; // Увеличиваем положение ячейки по вертикали.
iLeft := 0; // Обнуляем позицию ячейки по горизонтали.
End;
{----------------------------------- :9:}
ImageList1.GetBitmap(0, (FindComponent('A1') as TSpeedButton).Glyph); // Загружаем спрайт в первую ячейку.
Form2.FMineList.SaveToFile('OUTPUT.TXT'); // Сохраняем расположение мин в файл.
MineCountLabel.Caption := IntToStr(Form2.FMineList.Count); // Показываем колво мин на поле.
END;
Function TForm1.CheckMove(X,Y : ShortString): Boolean; // Функция проверки валидности ячейки. Она должна находится рядом с текущей.
Var
iXod, iAlf : Integer; // Вспомогательные переменные.
BEGIN
result := false; // Презумпция вины :)
iXod := Abs(StrToInt(Y) - StrToInt(dPos.yInt)); // Получаем расстояние до текущей ячейки по горизонтали.
iAlf := Abs(Ord(dPos.xChr) - Ord(X[1])); // Получаем расстояние до текущей ячейки по вертикали.
If ((iXod <= 1) and (iAlf <= 1)) and ((iXod <> 0) or (iAlf <> 0)) then result := true; // И если оно не больше одного, то разрешаем ход.
END;
Procedure TForm1.SpeedButton1Click(Sender: TObject); // Главная процедура нажатия на кнопку(ячейку).
Var
i : Integer; // Счётчик цикла.
cObject : TSpeedButton; // Сам объект.
Str, j : String; // Вспомогательная переменная имени ячейки.
Count : Integer; // Вспомогательная переменная. (стр. 160)
BEGIN
{Подготавливаем переменные :1:}
cObject := TSpeedButton(Sender); // Получаем ячейку, на которую кликнули.
Str := cObject.Name; // Присваиваем вспомогательной переменной имя задействованной ячейки.
dClick.xAlf := Str[1]; // Присваиваем переменной первый символ задействованной ячейки по вертикали.
dClick.yFlt := Copy(Str, 2, Length(Str)); // Получаем номер ячейки по горизонтали.
{------------------------- :1:}
...
По просьбам желающих. Код некогда созданной мною игрушки Сапёр. Отдельный прикол, это как потом все эти кнопки удаляются.
http://www.youtube.com/watch?v=xos2MnVxe-c
Блин, я думал что они вручную аккуратно разложены на форме... Цикл создания кнопок уменьшает градус говнокодности данного говокода :(
> Randomize
А вот Randomize перед каждым random'ом это то еще говнецо... Особенно если ГПСЧ инициализируется временем с точностью до мс...
bEgin~eNd //м-м даже так.
Вообще очень настроженно отношусь к коду в котором каждая строка раскомментирована. Но тут ничего смертельного. Так, мелочи.
Совсем не такое говно как рекламировали.
Alf сокращено от Alfavit. То есть координата клетки по вертикали. Flt координата по горизонтали. Это дополнительные значения координат.
Резонно.
> Flt координата по горизонтали.
А можно английское слово, от которого это порождено?
>X,Y : ShortString
Зачем координаты строками?
>iLeft := iLeft + 24
>SpeedButton[j].Left := iLeft+8;
Я такое в своё время тоже писал. Сейчас бы наверное вынес 8 в константу и танцевал от неё.
Помню в VB была херь TwipsPerPixel или как-то так. В дельфях тоже должно быть такое, 8 - это ж не пиксели? Вот от неё бы и мерял.
Сейчас уже не вспомню почему выбрал строки вместо чисел. Наверное, тогда мне показалось что так "компактней". Думаете есть смысл переписать под числа?
С меджик намберами тоже всё понятно.
Я бы не стал. Работает - не трогай.
Тупая работа вроде этой не принесет пользы.
Майкрософтовская системная венгерка жжот, кстати.
P.S. И интересно бы узнать что означают alf и flt?
А можно грабитьминировать корованы?
А то в коде говна я не вижу.
P.S. Присоединяюсь к предыдущему оратору с просьбой выложить куда-нибудь прогу.
Сапёр с сейвами это читерство!
Лучше вообще без комментариев, чем с такими.
for i := 1 to 10 do
swap(i, random(i+1,100));
Учи матан, гумно.
for i := 1 to 10 do
swap(i, random(i,100));
то есть клетку можно поменять с самой собой.
Иначе не сойдётся даже количество различных выхлопов ГСЧ за n итераций, будет (n-1)*(n-2)*...*(n-k)=C(n-1,k-1). А надо C(n,k)
Нет, это не C(n,k). Короче, если все мины разные, то число их расположений будет n*(n-1)*...(n-k+1)=n!/(n-k)! . И алгоритм мой тоже имеет столько же выходов в зависимости от результатов ГСЧ.
Чтобы доказать равномерность, надо доказать, что для каждого расположения мин существует выход алгоритма, дающий это расположение
Переход:
берём любое расположение k мин на поле из n клеток. Берём выход алгоритма k-1 мины для поля из n-1 клеток, который даёт нужное распложение для мин с номерами 2,3,4...k, такой выход существует по индукционному предположению.
Смотрим, какая клетка после данной перестановки переставилась туда, куда мы хотим поставить мину 1.
Тогда берём, ставим мину 1 на место этой клетки, делаем перестановку для (k-1) на (n-1), получаем нужную перестановку
чтд
> random(i,100)
С каких пор рандом принимает несколько аргументов? Или ты свою ф-ю писал? У меня не компилится короче.
Если бы я написал Random(cell_count-i+1)+i, то что, было бы понятнее?
1. Кидаем в первые N клеток бомбы (единицы):
2. Проходимся по всему массиву и каждый элемент меняем местами с другим случайным элементом:
Как можно быть таким невнимательным
Кстати, это всё для случая нумерации от 1, ну ты понял. Так паскальнее ^^
> High(MineArray)
> LengthOfArray
Ты определись
Использую переменную, вызывать лишний раз функцию ни к чему.
> случая нумерации от 1
А если юзать i := 0, какие подводные камни?
У тебя фигня в алгоритме же. В двух местах, в которых должна быть одна и та же сущность, я вижу разные вещи.
Что за High(), она же только для динмассива возвращает число используемых элементов. А в динмассиве с нуля начинается.
> А если юзать i := 0, какие подводные камни
Нумерацию сдвинуть трудно?
Так, сколько тебе лет, каков твой стаж?
> она же только для динмассива
MineArray у меня динамический массив.
В общем, спасибо. Всё работает как надо.
> Так, сколько тебе лет, каков твой стаж?
Нискажу.
И при этом ты так спокойно пошёл писать алгоритм, явно предназначенный для нумерации от 1?!
Ладно, подскажу, как вообще правильно надо действовать для любых индексов начала и конца:
2. Почему LengthOfArray хранит не длину, а верхний индекс? Назови нормально. LastIndex типа там.
Логика цикла перестановок правильная.
И ещё, я раньше тоже индексировал массивы от 0 до Length-1, а потом Ада меня отучила от этого.
Я тут заметил кое-что. Если использовать 1й вариант SwapEx (как в моём коде выше), то кол-во мин будет каждый раз варьироваться от 17 до 20. А если переписать:
то всё норм. Почему так?
Вот есть у тебя код
a:=a-b;
b:=a+b;
a:=b-a;
а теперь представь, что a и b являются ссылками на один и тот же адрес. После первой же строки они обе обнуляются.
Про индексы аналогично.
Про xor вместо +- аналогично.
вы оба занимаетесь преждевременной оптимизацией
да еще и на паскале
> Потому что ты пиздец
Два чаю этому сэру.
Речь идет о сферическом ньюфаге в вакууме.
P.S. А что это, если не форум?
> Кидаешь в первый 10 клеток
Куда конкретно кидать?
2. Если мин больше чем нужно - удаляешь случайную.
3. Если ровно столько сколько нужно - выходишь из цикла.
4. В противном случае переходишь на шаг 2.
Плюс алгоритма в простоте и том, что для входных данных только одно условие - число мин должно быть неотрицательным числом.
А как правильно брать случайные числа? Тут выше говорили что Randomize преде Random`ом говно.
Random сам по себе говно, ищи вихрь мерсена, а то как же играть в сапёра, без равномерного распределения мин?
А вот твистер из крестов с локальным состоянием в объекте или рандом в Хаскеле - норм
Не стоит подозревать. Там какой-нибудь тупейший конгруэнтный генератор в духе: Если есть желание - загляни в исходники делфевого RTL, благо они открыты.
То, что ты сказал, выдаст период длины max
И к тому же я забыл, как правильно преобразовывают генератор чисел от 0 до N в генератор от 0 до M ;(
Помню, что что-то типа буферной переменной, из которой делением извлекаются случайные числа нужного размера, а когда битов остается мало - в нее набиваются новые, взятые из генератора.
Не поверишь
Seed*M/N
В Паскале именно так и делается, так как там N=2^32, то делается mul, потому возвращается содержимое edx.
В сишке возвращаются старшие 16 бит сида, поэтому rand()%n не зацикливается слишком быстро.
Ну да, согласен, для конгруэнтного говногенератора такой способ более чем адекватен.
Но распределение получается чуть-чуть перекошенным. Если N не делится на M, то некоторые числа выпадают немного чаще остальных.
Портить хорошие генераторы типа вихря мерсена таким преобразованием нехорошо, поэтому я и пытался вспомнить как это делается с равномерным распределением (см. статью ниже).
http://mathforum.org/library/drmath/view/65653.html
The method does work, but... с какого для тасования колоды карт перестало хватать линейного конгруэнтного PNRG mod RANGE_MAX + RANGE_MIN?
Сначала прочитал как тарасования колоды
http://govnokod.ru/12107#comment160588
С того дня как всякие онлайн казино должны проходить сертификацию на труъ рандомность. Вот и приходится покупать аппаратные источники труъ рандома, и использовать такие методы...
ждем некогда созданную мною игрушку Сапёр онлайн, с мультиплеем и на бабки
Вроде как можно собрать самому даже. Нужен только АЦП, шумящий диод и контроллер. Для серьёзных применений не годится, конечно.
Кроме того, можно насолить впрок. Получил сорок гигабайт чистой энтропии и выбирай себе. Главное - не попадаться с этим правоохренителям на глаза.
Ну если надо было бы проходить сертификацию - я бы все-таки купил готовый.
P.S. Хотел одно время спаять такой генератор на стабилитроне - ни один стабилитрон шуметь не захотел ;(
Здесь как раз описан частный случай алгоритма, который приводил выше Тарас (ка пример правильного алгоритма). А так же описаны последствия хренового тасования: Авторы рассказывают, что написали программу, которая рассчитывала полный расклад, увидев первые пять карт, за одну секунду. А для покера это само собой эпик фейл.
Если их исправить, то рассказ в конце станет невероятным, т.к. нет реальной возможности узнать сколько было в LSB таймера на момент старта и сколько раундов RNG выполнилось до начала анализа.
На самом деле даже если пофиксить те баги, и оставить стандартный конгруэнтный PRNG, то проблема останется.
Перед началом алгоритма сид может находится в одном из 2^32 состояний, а
4млрд вариантов это не такое уж большое число. Это намного меньше, чем 52!.
Да, в точности угадать расклад уже не удастся. Если мы играем колодой из 52 карт, то 5 карт попадут нам на руки одним из 311875200 способов. Поделим 2^32 на это число, и получим в районе 14 раскладов. Для ускорения вычислений можно составить таблицу, пусть она и будет весить гигабайты, зато поиск будет мгновенным.
Итого - посмотрев на полученные карты, и не зная начального состояния генератора, мы получаем примерно 14 вариантов расклада. А это очень неплохой чит для такой игры как покер.
Я куплю тебе калач.
Если будешь плакать -
Куплю худой лапоть!
У тебя быдлоалгоритм, он тормозит, он долго пишется.
Мой алгоритм быстро работает, быстро пишется, даёт идеальное распределение, но чтобы это понять, нужен матан.
Это как сортировка перебором и квиксорт.
Это всё равно что удалять плохие элементы из массива по одному (со сдвигом хвоста), потому что это просто и понятно. А на просьбу сделать нормально (через проход с двумя итераторами) говорить, что это сложно и нарушает КИСС.
Я так понимаю, у быдлокодеров появился новый флаг?
Раньше они писали говно, потому что "Дейкстра сказал не оптимизировать", теперь они пишут говно, потому что "в Википедии написано не усложнять".
Какую идиому не придумай, а быдлокодер её всё равно в качестве отмазки применит.
Нейтрализовал.
> У тебя быдлоалгоритм, он тормозит, он долго пишется.
Может быть. Я предложил свою версию, почему ты в меня какашками кидаешься не знаю.
> Мой алгоритм быстро работает, быстро пишется
Да, я посмотрел твой алгоритм, действительно задумка хорошая. И он даже проще.
P.S. А людей незнакомых с ходу быдлокодерами называть перестань..
P.P.S. По-поводу "квадратичности" твой алгоритм отрабатывает n операций, где n - необходимое количество мин, а мой - (m-n), где n - количество мин, а m - площадь поля.
Таким образом, если нужно разместить 90 мин на поле в 100 клеток, мой алгоритм будет быстрее.
А если необходимо раместить 1 мину на поле в 1000 клеток - тут твой лидер.
Расскажи, как ты делаешь операцию "убрать случайную мину".
> P.S. А людей незнакомых с ходу быдлокодерами называть перестань..
Я тебя быдлокодером не называл, я сказал только, что у тебя быдлоалгоритм, и что быдлокодеры любую идиому применят в качестве оправдания своему быдлокоду.
Ну например так:
1. Заводим дополнительный массив, в который в начале алгоритма помещаем координаты всех мин;
2. Генерируем случайное число, используем его как индекс в массиве, получаем координаты мины которую надо удалить;
3. Берем последний элемент массива и переставляем его на место только что поюзанного;
4. ???
5. Удаление мин за О(1) при O(n) памяти, PROFIT
Т.е. сначала массив будет полностью забит единицами (бомбами), или как? И такой массив у меня уже есть, ничего заводить не надо.
http://govnokod.ru/12107#comment160674
> Куда конкретно кидать?
Хм. Очень странный вопрос. Как-нибудь нумеруете все клетки поля по порядку. В первые K клеток помещаете мины...
P.S. А если все-таки хочется поюзать алгоритм Fai - его лучше немного перевернуть:
1. Инициализируем само поле нулями, а дополнительный массив координатами всех клеток:
2. Для каждой мины
2.1. Генерируем случайное число rnd в диапазоне 1 . .freecnt (например 3)
2.2. free[rnd] покажет координаты клетки, в данном случае (2,1), помещаем в нее мину:
field = {{0, 0}, {1, 0}}
2.3. Переставляем free[freecnt] на место free[rnd], уменьшаем freecnt на единицу
P.P.S. Код приводить не буду из образовательных целей.
Зависть плохое чувство.
И алгоритм Fai не квадратичный (но n в О(n) побольше).
Расскажи мне про эту часть подробнее.
> 3. Если ровно столько сколько нужно - выходишь из цикла.
> цикла
http://ideone.com/dWAorT
> Плюс алгоритма
Плюс в том, что оригинальный баг починен и мины теперь кучкуются с другого края :-)