LCD1602 большие цифры

вид+ролик

Вся модель тетрашек это массив 5х6 где фишки цветные это 1,2,3,4…7. А пусто-место это 0. Механика это перестановка местами элементов массива в окрестностях 0 используемой в варианте игры навигацией (кнопки, сенсор, джойстик и т.д.).

…то есть сначала просто пишем массив, забиваем фишки в него и пишем функцию вывода на своё исполнительное уст-во (матрица ws2812b).

VID_20240612_113351 (1)

Если и буду делать, то на st7735 и джойстике с кнопкой внутри :slight_smile:

розовенький да, не удался,)))

это потому что я уже сонный был, пленка если что есть даже от монитора, которая превращает пиксели в кресты)))

Бумагу ещё подложить, белую, белую. Ну и попробовать вывод фигур по моим картинкам, будет видно или не видно тетрамино.

я пока что не знаю как код написать, но если 2 пустых места будет, то это явный конфликты и баги в коде в будущем…)))
но в принципе решаемо наверное, особенно если указывать куда поставить то что передвигаем

если вот так

Попытался представить “пятнашки” на поле 480х320.

andriano надеюсь написанную на языке го ? и запущенную на пк ?))) в принципе легко, но долго и беЗсмысленно))
зато как понятно написан код, надо брать всем фирмам на заметку, которые хотят что бы другие обслуживали их Г код за еду)))

а еще можно к ии потом обратится для сокращения!))

Начни вот так :slight_smile:

byte tetraschki[6][5]={{7,7,7,7,0},
                       {2,2,2,2,0},
                       {3,3,3,3,1},
                       {4,4,4,4,1},
                       {5,5,5,5,1},
                       {6,6,6,6,1}
                        };
 byte zero_1=0*5+4;//линейный номер в массиве первого пусто-места
 byte zero_2=1*5+4;//линейный номер в массиве второго пусто-места                       
void setup() {

  }
 void loop() {

}
///////////////////////

допустим сделаю, а тут l,l,l,l,l,l,l выигрышная комбинация после которой грузить одну из 5 следующих в фото ? 
+ добавить раскраску в рандомные цвета…вам вообще это пригодится ? 
вы же собрались делать это просто на tft дисплее, а у меня на адресной ленте)))
и если не пригодится,  тогда у меня вроде теряется смысл делать)))

не знаю получилось или нет,(переход на следующий левел) так как режим читерства я еще не сделал, 
а потенциометрами собирать не удобно))
#include <FastLED.h>

#define LED_PIN     6
#define NUM_LEDS    64
#define BUTTON_PIN  7

CRGB leds[NUM_LEDS];

const int MIN_X = 1;
const int MAX_X = 6;
const int MIN_Y = 1;
const int MAX_Y = 5;

const int POT_MIN = 100;
const int POT_MAX = 900;

int currentX = MIN_X;
int currentY = MIN_Y;

byte tetrawin[6][5] = {
{7,7,7,7,0},
{2,2,2,2,0},
{3,3,3,3,1},
{4,4,4,4,1},
{5,5,5,5,1},
{6,6,6,6,1}
};

byte tetra1[6][5] = {
{1,2,2,0,3},
{1,2,2,3,3},
{1,1,5,3,4},
{6,5,5,7,4},
{6,6,5,7,4},
{0,6,7,7,4}
};

byte tetra2[6][5] = {
{1,1,0,2,2},
{1,1,3,2,2},
{4,4,3,5,5},
{4,4,3,5,5},
{6,6,3,7,7},
{6,6,0,7,7}
};

byte tetra3[6][5] = {
{1,2,2,2,3},
{1,1,2,3,3},
{1,0,4,0,3},
{6,4,4,4,5},
{6,6,7,5,5},
{6,7,7,7,5}
};

byte tetra4[6][5] = {
{1,1,2,2,2},
{1,3,0,0,2},
{1,3,4,5,5},
{3,3,4,5,6},
{7,4,4,5,6},
{7,7,7,6,6}
};

byte tetra5[6][5] = {
{1,1,1,0,2},
{1,3,2,2,2},
{7,3,0,4,4},
{7,3,3,5,4},
{7,7,6,5,4},
{6,6,6,5,5}
};

byte (*currentLevel)[5] = tetra1;
int currentLevelNumber = 1;
bool isWinLevel = false;

struct MovablePixel {
int x;
int y;
CRGB color;
bool isCarried;
byte originalColorNum;
};

MovablePixel pixels[30];
int NUM_PIXELS = 0;

bool lastButtonState = HIGH;
bool buttonPressed = false;
int carriedPixelIndex = -1;

CRGB numberToColor(byte num) {
switch(num) {
case 0: return CRGB::Black;
case 1: return CRGB::Red;
case 2: return CRGB::Blue;
case 3: return CRGB::Green;
case 4: return CRGB::Yellow;
case 5: return CRGB::Purple;
case 6: return CRGB::Cyan;
case 7: return CRGB::White;
default: return CRGB::Black;
}
}

void loadLevel(byte level[6][5], bool isWinLevel = false) {
NUM_PIXELS = 0;

if (isWinLevel) {
for (int x = 0; x < 6; x++) {
for (int y = 0; y < 5; y++) {
byte colorNum = level[x][y];
if (colorNum != 0) {
pixels[NUM_PIXELS].x = x + 1;
pixels[NUM_PIXELS].y = y + 1;
pixels[NUM_PIXELS].color = numberToColor(colorNum);
pixels[NUM_PIXELS].originalColorNum = colorNum;
pixels[NUM_PIXELS].isCarried = false;
NUM_PIXELS++;
}
}
}
} else {
// Находим уникальные номера фигур в уровне
byte uniqueFigures[8] = {0};
byte figureCount = 0;
for (int x = 0; x < 6; x++) {
for (int y = 0; y < 5; y++) {
byte figNum = level[x][y];
if (figNum != 0) {
bool found = false;
for (int i = 0; i < figureCount; i++) {
if (uniqueFigures[i] == figNum) {
found = true;
break;
}
}
if (!found) {
uniqueFigures[figureCount++] = figNum;
}
}
}
}

// Перемешиваем цвета 1-7
byte colors[7] = {1,2,3,4,5,6,7};
for (int i = 0; i < 7; i++) {
int j = random(7);
byte temp = colors[i];
colors[i] = colors[j];
colors[j] = temp;
}

// Создаем карту цветов для фигур
byte colorMap[8] = {0};
for (int i = 0; i < figureCount; i++) {
colorMap[uniqueFigures[i]] = colors[i % 7];
}

// Создаем пиксели с правильными цветами
for (int x = 0; x < 6; x++) {
for (int y = 0; y < 5; y++) {
byte originalColorNum = level[x][y];
if (originalColorNum != 0) {
pixels[NUM_PIXELS].x = x + 1;
pixels[NUM_PIXELS].y = y + 1;
pixels[NUM_PIXELS].color = numberToColor(colorMap[originalColorNum]);
pixels[NUM_PIXELS].originalColorNum = originalColorNum;
pixels[NUM_PIXELS].isCarried = false;
NUM_PIXELS++;
}
}
}
}
}

bool checkWinCondition() {
bool occupied[6][5] = {false};

for (int i = 0; i < NUM_PIXELS; i++) {
if (!pixels[i].isCarried) {
int x = pixels[i].x - 1;
int y = pixels[i].y - 1;
if (x >= 0 && x < 6 && y >= 0 && y < 5) {
occupied[x][y] = true;
}
}
}

for (int x = 0; x < 6; x++) {
for (int y = 0; y < 5; y++) {
bool shouldBeOccupied = (tetrawin[x][y] != 0);
bool isOccupied = occupied[x][y];

if (shouldBeOccupied != isOccupied) { return false; }
}
}
return true;
}

void loadRandomTetraLevel() {
int randomLevel = random(1, 6);

switch(randomLevel) {
case 1: currentLevel = tetra1; break;
case 2: currentLevel = tetra2; break;
case 3: currentLevel = tetra3; break;
case 4: currentLevel = tetra4; break;
case 5: currentLevel = tetra5; break;
}

currentLevelNumber = randomLevel;
isWinLevel = false;
loadLevel(currentLevel, false);

Serial.print("Загружен уровень tetra");
Serial.print(randomLevel);
Serial.println(" - все 7 цветов распределены по фигурам");
}

void switchLevel() {
if (isWinLevel) {
loadRandomTetraLevel();
} else {
currentLevel = tetrawin;
isWinLevel = true;
loadLevel(currentLevel, true);
Serial.println("Загружен уровень tetrawin");
}
}

void setup() {
Serial.begin(9600);
randomSeed(analogRead(0));
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(50);
pinMode(BUTTON_PIN, INPUT_PULLUP);

loadRandomTetraLevel();

updateDisplay();
Serial.println("Система готова. Соберите tetrawin для перехода на следующий уровень");
}

void loop() {
int potX = analogRead(A2);
int potY = analogRead(A1);

int newX = convertToCoordinate(potX, MIN_X, MAX_X);
int newY = convertToCoordinate(potY, MIN_Y, MAX_Y);

handleButton();

if (buttonPressed) {
if (carriedPixelIndex == -1) {
for (int i = 0; i < NUM_PIXELS; i++) {
if (!pixels[i].isCarried && newX == pixels[i].x && newY == pixels[i].y) {
carriedPixelIndex = i;
pixels[i].isCarried = true;
updateDisplay();
break;
}
}
} else {
if (canPlacePixel(currentX, currentY)) {
pixels[carriedPixelIndex].isCarried = false;
pixels[carriedPixelIndex].x = currentX;
pixels[carriedPixelIndex].y = currentY;
carriedPixelIndex = -1;
updateDisplay();

if (checkWinCondition()) {
if (isWinLevel) {
Serial.println("=== ПОБЕДА! Переход на уровень tetra ===");
} else {
Serial.println("=== ПОБЕДА! Переход на уровень tetrawin ===");
}
delay(2000);
switchLevel();
}
} else {
Serial.println("Нельзя поставить пиксель - ячейка занята!");
}
}
}

if (newX != currentX || newY != currentY) {
currentX = newX;
currentY = newY;
updateDisplay();
}

delay(100);
}

bool canPlacePixel(int x, int y) {
for (int i = 0; i < NUM_PIXELS; i++) {
if (!pixels[i].isCarried && pixels[i].x == x && pixels[i].y == y) {
return false;
}
}
return true;
}

void handleButton() {
bool currentButtonState = digitalRead(BUTTON_PIN);

if (lastButtonState == HIGH && currentButtonState == LOW) {
buttonPressed = true;
delay(50);
} else {
buttonPressed = false;
}
lastButtonState = currentButtonState;
}

void updateDisplay() {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black;
}
for (int i = 0; i < NUM_PIXELS; i++) {
if (!pixels[i].isCarried) {
setPixel(pixels[i].x, pixels[i].y, pixels[i].color);
}
}

if (carriedPixelIndex != -1) {
setPixel(currentX, currentY, pixels[carriedPixelIndex].color);
} else {
setPixel(currentX, currentY, CRGB::Black);
}
FastLED.show();
}

int convertToCoordinate(int potValue, int minCoord, int maxCoord) {
potValue = constrain(potValue, POT_MIN, POT_MAX);
int range = maxCoord - minCoord + 1;
int scaled = (potValue - POT_MIN) * range;
int result = minCoord + scaled / (POT_MAX - POT_MIN + 1);
return constrain(result, minCoord, maxCoord);
}

void setPixel(int x, int y, CRGB color) {
leds[y * 8 + x] = color;
}

А сделать самому законченный макет изделия и выложить в проектах - бессмысленно?

…если тревожат средства управления и индикации можно обойтись без них. Использовать только уно и монитор порта в качестве управления и индикации.

…какой-нибудь вариант псевдографики для отрисовки тетрашек и оригинальный способ команд в строку.

…на начальном этапе достаточно просто механики игры, а не алгоритмов отработки правильности решения, индикации игрового прогресса и т.д.

Вот для старта, на примере игры пятнашки. Загрузи в уно, посмотри псевдографику.

…если есть понимание допиши механику перестановок фишек по типу так - вводим номер фишки рядом с пусто-местом расположенной и она меняется местами с пусто-местом.

byte pitnaschki[4][4]={{1, 2, 3, 4 },
                       {5, 6, 7, 8 },
                       {9,10,11, 12},
                       {13,14,15,0 },
                        };
 byte zero_=3*4+3;//линейный номер в массиве пусто-места
                   
void setup() {
Serial.begin(9600);
}
 void loop() {
otrisovka();
delay(1000);
}
///////////////////////
void otrisovka(){
 for(int i=0;i<4;i++){
 for(int j=0;j<4;j++){
 if(pitnaschki[i][j]!=0&&pitnaschki[i][j]<10){Serial.print("[ ");Serial.print(pitnaschki[i][j]);Serial.print(']');}
 if(pitnaschki[i][j]!=0&&pitnaschki[i][j]>=10){Serial.print('[');Serial.print(pitnaschki[i][j]);Serial.print(']');}
 if(pitnaschki[i][j]==0){Serial.print("[  ]");} 
 } 
  Serial.println();
}
 Serial.println("////////////////"); 
}
///////////////////////

…даже фотки делать не надо :), только скрины экрана-порта.

А “питнащки” (pitnaschki) - это чей акцент?

Еёйный

:slight_smile: Вот вы тёмные. Буква щ пишется как schtsch, про букву я вообще молчу. Европа, что с неё взять?

…типа ja? Вообще я по немецки это ich как слово, а по звуку я это да по немецки. Короче туго у них с этой буквой.

Ихнянский язык он многобуквенный и многословный.

…они не скажут отечество, а скажут папастрана.

…да и деда у них нет, так Большойпапа.

Вообще конечно Tag games в исходнике.

Не надо. И вправду ИИ могёть писать скетчи с одной фразы-задания :slight_smile:

В образовательном плане конечно офигенно!!!

Спойлер
// 15-puzzle for Arduino Serial Monitor
int board[4][4];            // игровое поле 4x4, 0 обозначает пустую клетку
int emptyR = 3, emptyC = 3; // координаты пустой клетки (строка, столбец), по умолчанию в правом нижнем углу

void setupBoardSolved() {   // инициализация поля в решённом состоянии
  int v = 1;               // значение первой плитки
  for (int r = 0; r < 4; ++r) // по строкам
    for (int c = 0; c < 4; ++c) // по столбцам
      board[r][c] = (r == 3 && c == 3) ? 0 : v++; // заполняем числами 1..15, последний = 0 (пусто)
  emptyR = 3; emptyC = 3;  // устанавливаем координаты пустой клетки
}

void printBoard() {        // вывод поля в Serial Monitor
  Serial.println(F("---------------------")); // граница таблицы
  for (int r = 0; r < 4; ++r) { // для каждой строки
    for (int c = 0; c < 4; ++c) { // для каждого столбца
      if (board[r][c] == 0) Serial.print("   "); // пустая клетка выводится как пробелы
      else {
        if (board[r][c] < 10) Serial.print(' '); // выравнивание для однозначных чисел
        Serial.print(board[r][c]); // вывод значения плитки
        Serial.print(' ');         // пробел между плитками
      }
    }
    Serial.println(); // перевод строки после строки поля
  }
  Serial.println(F("---------------------")); // нижняя граница таблицы
}

bool tryMoveTile(int num) { // попытка сдвинуть плитку с номером num
  if (num < 1 || num > 15) return false; // проверка диапазона номера
  int tr=-1, tc=-1;       // координаты найденной плитки (-1 = не найдена)
  for (int r=0;r<4;++r)   // ищем плитку с этим номером на поле
    for (int c=0;c<4;++c)
      if (board[r][c] == num) { tr=r; tc=c; }
  if (tr<0) return false; // если не нашли — возвращаем false
  // проверяем соседство с пустой клеткой
  if ((abs(tr - emptyR) == 1 && tc == emptyC) || (abs(tc - emptyC) == 1 && tr == emptyR)) {
    board[emptyR][emptyC] = board[tr][tc]; // перемещаем плитку в пустую клетку
    board[tr][tc] = 0;                     // старая позиция становится пустой
    emptyR = tr; emptyC = tc;              // обновляем координаты пустой клетки
    return true;                           // ход выполнен успешно
  }
  return false; // ход невозможен (плитка не соседняя)
}

bool isSolved() {         // проверка, решена ли головоломка
  int v = 1;              // ожидаемое значение первой плитки
  for (int r = 0; r < 4; ++r)
    for (int c = 0; c < 4; ++c) {
      if (r == 3 && c == 3) return board[r][c] == 0; // последняя клетка должна быть пустой
      if (board[r][c] != v++) return false; // если значение не соответствует ожидаемому — не решено
    }
  return true; // (формально недостижимо, т.к. цикл возвращает на последней клетке)
}

void shuffleBoard(int moves) { // перемешивание поля выполнением случайных допустимых ходов
  for (int i = 0; i < moves; ++i) {
    int dirs[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; // смещения для соседних клеток (вверх, вниз, влево, вправо)
    int options[4][2]; // возможные координаты для перемещения в пустую
    int optCount = 0;  // количество возможных ходов
    for (int d = 0; d < 4; ++d) { // проверяем каждое направление
      int nr = emptyR + dirs[d][0]; // новая строка
      int nc = emptyC + dirs[d][1]; // новый столбец
      if (nr >= 0 && nr < 4 && nc >= 0 && nc < 4) { // если внутри поля
        options[optCount][0] = nr; // сохраняем опцию
        options[optCount][1] = nc;
        optCount++; // увеличиваем счётчик опций
      }
    }
    int pick = random(optCount); // случайно выбираем одну из опций
    int pr = options[pick][0];   // выбранная строка
    int pc = options[pick][1];   // выбранный столбец
    // перемещаем выбранную соседнюю плитку в пустую клетку
    board[emptyR][emptyC] = board[pr][pc];
    board[pr][pc] = 0;
    emptyR = pr; emptyC = pc; // обновляем координаты пустой клетки
  }
}

void setup() {
  Serial.begin(9600);           // инициализируем Serial с скоростью 9600 бод
  while (!Serial) ;             // ожидаем готовности Serial (важно для плат с USB)
  randomSeed(analogRead(A0) ^ micros()); // сеем генератор случайных чисел шумом с A0 и микросекундами
  setupBoardSolved();           // создаём начальное решённое поле
  Serial.println(F("Пятнашки через Serial Monitor")); // приветственное сообщение
  Serial.println(F("Введите номер плитки (1-15) чтобы её сдвинуть.")); // инструкция
  Serial.println(F("Команды: s - перемешать, r - сбросить в исходное состояние.")); // команды
  printBoard();                 // выводим начальное поле
}

String readLine() {             // читаем строку из Serial (с учётом '\n')
  if (!Serial.available()) return String(); // если данных нет — возвращаем пустую строку
  String s = Serial.readStringUntil('\n'); // читаем до перевода строки
  s.trim();                   // убираем пробелы и CR в начале/конце
  return s;                   // возвращаем прочитанную строку
}

void loop() {
  String cmd = readLine();     // получаем команду от пользователя
  if (cmd.length() == 0) return; // если ничего не введено — ждём дальше
  if (cmd.equalsIgnoreCase("s")) { // команда 's' — перемешать поле
    shuffleBoard(200);         // делаем 200 случайных допустимых перемещений
    Serial.println(F("Перемешано.")); // уведомление об успешном перемешивании
    printBoard();              // выводим поле
    return;
  }
  if (cmd.equalsIgnoreCase("r")) { // команда 'r' — сброс в исходное состояние
    setupBoardSolved();         // восстанавливаем решённое поле
    Serial.println(F("Сброшено в исходное состояние.")); // уведомление
    printBoard();               // выводим поле
    return;
  }
  // проверяем, является ли ввод числом
  bool ok = true;
  for (unsigned int i=0;i<cmd.length();++i) {
    if (!isDigit(cmd.charAt(i))) { ok = false; break; } // если встречен нецифровой символ — не число
  }
  if (ok && cmd.length() > 0) { // если ввели число
    int num = cmd.toInt();      // преобразуем строку в число
    if (tryMoveTile(num)) {     // пытаемся выполнить ход
      printBoard();             // выводим поле после хода
      if (isSolved()) {         // если поле решено
        Serial.println(F("Поздравляем! Вы решили головоломку!")); // поздравление
        Serial.println(F("Введите s чтобы перемешать, r чтобы начать заново.")); // подсказка следующих действий
      }
    } else {
      Serial.println(F("Невозможный ход. Введите номер плитки, соседней с пустой.")); // ошибка хода
    }
  } else {
    Serial.println(F("Неизвестная команда.")); // ввод не распознан как
}
}

именно так, не думаю что слишком популярный проект будет,(особенно со способом управления, ком порт это не совсем то, но как вариант))) и выкладывать надо готовые инструменты, например декодирование-воспроизведение мп3, где каждый сможет это в свой проект подтянуть, и остальной код для себя написать)))

Ну а вообще проекты на основе ИИ публичные не хочешь делать?

…в хороших фильмах ИИ всегда в космосе корабли ведут, а экипажи так, пьют коктейли :slight_smile:

что то бескорыстно делать совсем не интересно, ну разве кодом поделиться с тем кто и так умеет его писать))
работу возможно было бы интересно найти тут, но у меня сверхзапросы, мне бы просто вакансию за 150к на удаленке найти, при этом что бы собеседование не проходить у сюзанны, или не стоять на коленях на собеседовании,(за жалких 150к а не обычных 500к) и в целом не разгребать косяки с кодом,(бесплатно, это когда они не наняли специалиста, а наняли кого попало, и проще переписать проекты), и главное что бы не слушать постоянное нытье работодателя,(а скорее ультимативную форму)) ) что надо снова писать код за еду, и новый проект пилить, когда он не способен продавать продукт, и приносить %))) так как альтернатива работать обычным курьером, за 80к, не нести убытки и не набирать лишний вес, и главное не унижаться, гораздо заманчивее))) но программирование мк у меня постольку поскольку… я больше робототехник… и в серьез не рассматриваю программирование мк, особенно тут)))

но я то ужасный человек, с черным и алчным сердцем!))) может вы поделитесь мнением зачем это вообще делать ? мне это как то не понятно…

Это из той же оперы, почему линукс бесплатный, а винда за большие деньги продаётся. Самоутвердиться (во как я могу, а вам слабо?). Именно по этому линукс жив и популярен. Энтузиазм ни кто не отменял.