#include <stdlib.h>
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "Adafruit_NeoPixel.h"
#define NEOPIXEL_PIN 33
#define NUM_PIXELS 16
#define P1_OPPONENT_PIN 5
#define P1_SELF_PIN 6
#define P2_OPPONENT_PIN 7
#define P2_SELF_PIN 8
// tft color display
#define TFT_OLIVE 0x7BE0
#define TFT_BLACK 0x0000
#define TFT_BLUE 0x001F
#define TFT_RED 0xF800
#define TFT_GREEN 0x07E0
#define TFT_CYAN 0x07FF
#define TFT_MAGENTA 0xF81F
#define TFT_YELLOW 0xFFE0
#define TFT_WHITE 0xFFFF
//CS , DC
#define TFT1_DC 2
#define TFT1_CS 15
#define TFT2_DC 1
#define TFT2_CS 16
Adafruit_ILI9341 tft1 = Adafruit_ILI9341(TFT1_CS, TFT1_DC);
Adafruit_ILI9341 tft2 = Adafruit_ILI9341(TFT2_CS, TFT2_DC);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
//functions
void shuffleArray(int arr[], int startIdx, int endIdx);
void ShowBulletArray(bool showState, bool fade);
void NewBulletArray();
void ShuffleBulletArray();
void handleShooting(bool atOpponent);
void NewRound();
void UpdateDisplays();
bool bulletFired = false;
bool shotAtOpponent = false;
// Bullet data
int BulletArray[16];
int TotalBulletAmount, RealBulletAmount, AvailableBulletIndex;
// Player data
int Player1GadgetSlot[6], Player2GadgetSlot[6];
// Gadget
// Present Round data
int Round=0, Restrictions[8], PlayerTurn=1, P1Health=1, P2Health=1;
enum Status
{
P1turn,
P2turn,
RoundEnd,
IDLE
} state=IDLE;
// Background Data
const int RoundHealthData[3]={5,4,3}; // player's initial health in every round
const int RoundBulletData[3]={5,4,3}; // player's initial health in every round
void setup() {
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(18), CheckPlayerInput, RISING);
// initialize WS2812 pixel array display
pixels.begin();
// TFT display initialization
tft1.begin();
tft1.setRotation(3); // Screen rotation
tft2.begin();
tft2.setRotation(3); // Screen rotation
tft1.fillScreen(TFT_BLACK);
tft2.fillScreen(TFT_BLACK);
// internal input pullup resistor for user-end selection
pinMode(P1_OPPONENT_PIN, INPUT);
pinMode(P1_SELF_PIN, INPUT);
pinMode(P2_OPPONENT_PIN, INPUT);
pinMode(P2_SELF_PIN, INPUT);
tone(19,500,250);
randomSeed(analogRead(A0));
NewRound();
DisplayInformation();
DisplayCharacters();
delay(1000);
}
void loop() {
tft1.fillScreen(TFT_BLACK);
UpdateDisplays();
delay(50);
tft2.fillScreen(TFT_BLACK);
UpdateDisplays();
delay(50);
if(--P1Health<1){P1Health=5;}
}
void NewRound(){
Round++;
P1Health=RoundHealthData[Round-1];
P2Health=RoundHealthData[Round-1];
DisplayPlayersHealth(P1Health, P2Health);
NewBulletArray();
ShowBulletArray(true,false);
delay(5000);
ShowBulletArray(false,true);
}
void CheckPlayerInput(){
if (PlayerTurn==1){
if (digitalRead(P1_OPPONENT_PIN) == 1) {
handleShooting(true);
delay(1000);
}
if (digitalRead(P1_SELF_PIN) == 1) {
handleShooting(false);
delay(1000);
}
}else if(PlayerTurn==2){
if (digitalRead(P2_OPPONENT_PIN) == 1) {
handleShooting(true);
delay(1000);
}
if (digitalRead(P2_SELF_PIN) == 1) {
handleShooting(false);
delay(1000);
}
}
tone(19,500,250);
}
void ShowBulletArray(bool showState, bool fade){
if (showState){
for (int i = 0; i < NUM_PIXELS; i++) {
if (BulletArray[i] == 0) {
pixels.setPixelColor(i, pixels.Color(255, 0, 0));// 真彈,顯示紅色
} else if (BulletArray[i] == 1) {
pixels.setPixelColor(i, pixels.Color(0, 0, 255));// 空包彈,顯示藍色
} else {
pixels.setPixelColor(i, pixels.Color(0, 0, 0));
}
}
}else{
if (fade){
for (int i=TotalBulletAmount-1;i>=0;i--){
pixels.setPixelColor(i, pixels.Color(0, 0, 0));// blank
pixels.show();
delay(100);
}
}else{
for (int i = 0; i < NUM_PIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(0, 0, 0));// blank
}
}
}
pixels.show();
}
void NewBulletArray() {
//int number = random(3, NUM_PIXELS); // 隨機生成子彈總數
int number = RoundBulletData[Round-1];
int real = random(1, number); // 隨機生成真彈數量
// extreme case prevention
if((number-real<4)&& (number>8)){
real-=random(number/6, number/2);
}
if((number-real>8)&&(real<4)){
real+=random(number/6, number/2);
}
// error adjust
if (real<1){
real = 1;
}
if (real>=number){
real = number-1;
}
RealBulletAmount = real;
TotalBulletAmount = number;
AvailableBulletIndex = 0;
for (int i = 0; i < NUM_PIXELS; i++) {
if (i < number) {
if (i < real) {
BulletArray[i] = 0; // real bullet
} else {
BulletArray[i] = 1; // dummy round
}
} else {
BulletArray[i] = 2; // blank round
}
}
shuffleArray(BulletArray, 0, number - 1);
}
void ShuffleBulletArray(){
shuffleArray(BulletArray, 0, TotalBulletAmount - 1);
}
void shuffleArray(int arr[], int startIdx, int endIdx) {
// Calculate the size of the portion to shuffle
int size = endIdx - startIdx + 1;
// Shuffle the portion of the array
for (int i = startIdx; i <= endIdx; i++) {
int randomIdx = startIdx + rand() % size; // Generate a random index within the portion
// Swap the current element with a randomly selected element within the portion
int temp = arr[i];
arr[i] = arr[randomIdx];
arr[randomIdx] = temp;
}
}
void handleShooting(bool atOpponent) {
if (AvailableBulletIndex<TotalBulletAmount){ // check if there's ammo left
if (atOpponent) {
// 射擊對手的邏輯
if (BulletArray[AvailableBulletIndex] == 0) {
Serial.println("Opponent hit! It's a real bullet.");
} else {
Serial.println("Opponent is safe! It's a blank round.");
}
} else {
// 射擊自己的邏輯
if (BulletArray[AvailableBulletIndex] == 0) {
Serial.println("You shot yourself! It's a real bullet.");
} else {
Serial.println("You survived! It's a blank round.");
}
}
DisplayPlayersHealth(P1Health, P2Health);
}
if (AvailableBulletIndex>=TotalBulletAmount){ // check finished
Serial.println("Magazine emptied, Round finished.");
}else{
AvailableBulletIndex++;
}
}
void UpdateDisplays(){
tft1.fillScreen(TFT_BLACK);
tft2.fillScreen(TFT_BLACK);
DisplayCharacters();
DisplayInformation();
DisplayPlayersHealth(P1Health, P2Health);
}
void DisplayCharacters(){
tft1.setCursor(280, 100);
tft1.setTextColor(TFT_RED);
tft1.setTextSize(2);
tft1.println("EN");
tft1.setCursor(140, 200);
tft1.println("SF");
tft2.setCursor(280, 100);
tft2.setTextColor(TFT_RED);
tft2.setTextSize(2);
tft2.println("EN");
tft2.setCursor(140, 200);
tft2.println("SF");
}
//void DisplayState()
void DisplayInformation(){
tft1.setCursor(5, 5);
tft1.setTextColor(TFT_RED);
tft1.setTextSize(2);
tft1.println("Russian Roulette");
tft1.setCursor(5, 25);
tft1.println("Player 1");
tft1.setCursor(5, 45);
tft1.print("Round ");tft1.println(Round);
tft1.drawRect(120,90,200,140,TFT_RED);
tft2.setCursor(5, 5);
tft2.setTextColor(TFT_RED);
tft2.setTextSize(2);
tft2.println("Russian Roulette");
tft2.setCursor(5, 25);
tft2.println("Player 2");
tft2.setCursor(5, 45);
tft2.print("Round ");tft2.println(Round);
tft2.drawRect(120,90,200,140,TFT_RED);
}
void DisplayPlayersHealth(int Player1Health, int Player2Health){
tft1.setTextSize(2);
tft1.setTextColor(TFT_OLIVE);
tft1.setCursor(120, 70);
tft1.print("E:");
tft1.setTextColor(TFT_RED);
for(int i=0;i<Player2Health;i++){
tft1.print("Y");
}
tft1.setTextColor(TFT_OLIVE);
tft1.setCursor(220, 70);
tft1.print("S:");
tft1.setTextColor(TFT_RED);
for(int i=0;i<Player1Health;i++){
tft1.print("Y");
}
tft2.setTextSize(2);
tft2.setTextColor(TFT_OLIVE);
tft2.setCursor(120, 70);
tft2.print("E:");
tft2.setTextColor(TFT_RED);
for(int i=0;i<Player1Health;i++){
tft2.print("Y");
}
tft2.setTextColor(TFT_OLIVE);
tft2.setCursor(220, 70);
tft2.print("S:");
tft2.setTextColor(TFT_RED);
for(int i=0;i<Player2Health;i++){
tft2.print("Y");
}
}
Loading
esp32-s2-devkitm-1
esp32-s2-devkitm-1