Если я напишу 2, потом 4, потом 6, тогда мы почувствуем себя хорошо, потому что мы знаем, что дальше идет 8. Мы можем это предвидеть, мы не в руках судьбы. Однако, к сожалению, это не имеет ничего общего с истиной...
х/ф «Оксфордские убийства»
Задача: обнаружить центральную фигуру среди сереньких фигур вокруг.
Данный проект — PsyMatchArea — задумывался в качестве альтернативы знаменитым таблицам Шульте. С теми же целями (тренировка зрительного периферического внимания), но с другими, более «отмороженными» исходными предпосылками. Во-первых, нужно было уйти от цифр и букв — символов, знакомых каждому практически с яслей и потому распознаваемых на автомате без активного вовлечения сознания в процесс. Во-вторых, чтобы внимание как можно меньше подменялось памятью (и вообще не расслаблялось), надо было обеспечить соответствующие «помехи» — смена позиций, мерцания, наложения и т.п.
Режим с несколькими одновременно включёнными помехами
Самым, наверное, интересным с точки зрения программирования было создать алгоритм, который бы выдавал достаточно разнообразное множество абстрактных символов того или иного типа. Например, символы типа «руна» выращиваются следующим образом: алгоритму на входе предлагается сгенерировать в узловой матрице 3 x 3 пять последовательных соединений с некоторыми топологическими «принуждениями» (ограничение на количество пересечений и повышенная вероятность зацепа всех сторон периметра матрицы). Если в течение определённого количества попыток алгоритм заходит в тупик, он проявляет «толерантность» и пропускает к выходу последний тупиковый вариант (как говорится, что выросло, то выросло).
Код генератора рун
void CRunePattern::genSolidLink(int linkCount, int crossesCount)
{
if (this->isEmpty())
return;
int broadCharge = 5;
while (broadCharge) {
m_links.clear();
QVector <int> dots;
int currDot = qrand()%(m_cols*m_rows);
for (int i = 0; i < linkCount; ++i)
{
dots.append(currDot);
int crossCharge = 5;
while (crossCharge) {
int nextDotCharge = 5;
int nextDot = qrand()%(m_cols*m_rows);
while (nextDotCharge && dots.contains(nextDot)) {
nextDot = qrand()%(m_cols*m_rows);
nextDotCharge --;
}
if (nextDotCharge) {
CDotLink newLink = CDotLink(currDot, nextDot);
if (this->hasCrossLink(newLink)) {
if (crossesCount) {
crossesCount --;
} else {
// find another link that is'nt crosses with others
crossCharge --;
continue;
}
}
m_links.append(newLink);
currDot = nextDot;
}
break;
}
}
int maxRow = -1;
int minRow = m_rows;
int maxCol = -1;
int minCol = m_rows;
for (int d = 0; d < dots.count(); ++d) {
if (rowByDot(dots.at(d)) < minRow)
minRow = rowByDot(dots.at(d));
if (rowByDot(dots.at(d)) > maxRow)
maxRow = rowByDot(dots.at(d));
if (colByDot(dots.at(d)) < minCol)
minCol = colByDot(dots.at(d));
if (colByDot(dots.at(d)) > maxCol)
maxCol = colByDot(dots.at(d));
}
if (minCol == 0 && minRow == 0 &&
maxRow == m_rows - 1 && maxCol == m_cols - 1)
break;
broadCharge --;
continue;
}
}
На данный момент программа способна генерировать знаки нескольких типов:
Так же были реализованы следующие помехи:
- Swap interference — перетасовка фигур
- Hide center pattern — исчезновение центральной фигуры
- Thrill interference — дребезжание фигур
- Color interference — цветовое мерцание (гирлянда)
- Cover interference — концентрические окружности
Кроме того, предусмотрена возможность включения подсказки — Tip Settings (искомая фигура «подсвечивается» через указанное время).
Интерфейс настроек
После прохождения таблицы показывается статистика. По горизонтали угадывания, по вертикали время. Зелёный цвет — правильный ответ, красный — неверный, жёлтый — была подсказка.
Отображение статистики
Пара методических замечаний. Не стоит искать фигуру, разбрасывая взгляд во всех направлениях — это, во-первых, быстро вымотает и, во-вторых, ничего не даст в плане развития фонового внимания. Желательно расслабиться и рассредоточить локус зрительного внимания по всему окну, держа «в уме» искомую фигуру.
Исходный код на GitHub (Qt 4.8)
Сборка под Win32