8-BT7Tz6YA20nvp0M.png

  1. C++编程比赛题目:编写一个程序,接受一组数字的输入,然后统计并输出不超过这组数字的平均值的数字的个数
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "请输入数字的数量: ";
    cin >> n;

    // 声明一个数组来存储输入的数字
    int numbers[n];
    int sum = 0;

    // 输入数字
    for (int i = 0; i < n; i++) {
        cout << "请输入第 " << i + 1 << " 个数字: ";
        cin >> numbers[i];
        sum += numbers[i];
    }

    // 计算平均值
    double average = static_cast<double>(sum) / n;

    // 统计不超过平均值的数字的个数
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (numbers[i] <= average) {
            count++;
        }
    }

    // 输出结果
    cout << "平均值为: " << average << endl;
    cout << "不超过平均值的数字个数为: " << count << endl;

    return 0;
}
  1. 输入一些数,求第二大的值
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "请输入数字的数量: ";
    cin >> n;

    if (n < 2) {
        cout << "至少需要输入两个数字才能找到第二大值。" << endl;
        return 1;
    }

    int first_max = INT_MIN;  // 初始最大值为负无穷
    int second_max = INT_MIN; // 初始第二大值为负无穷

    for (int i = 0; i < n; i++) {
        int num;
        cout << "请输入第 " << i + 1 << " 个数字: ";
        cin >> num;

        if (num > first_max) {
            second_max = first_max;
            first_max = num;
        } else if (num > second_max && num != first_max) {
            second_max = num;
        }
    }

    if (second_max == INT_MIN) {
        cout << "没有找到第二大值。" << endl;
    } else {
        cout << "第二大值为: " << second_max << endl;
    }

    return 0;
}
  1. 输入一些数,哪两个数最接近
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int main() {
    int n;
    cout << "请输入数字的数量: ";
    cin >> n;

    if (n < 2) {
        cout << "至少需要输入两个数字才能找到最接近的两个数字。" << endl;
        return 1;
    }

    vector<int> numbers;
    for (int i = 0; i < n; i++) {
        int num;
        cout << "请输入第 " << i + 1 << " 个数字: ";
        cin >> num;
        numbers.push_back(num);
    }

    int minDiff = abs(numbers[0] - numbers[1]);
    int closest1 = numbers[0];
    int closest2 = numbers[1];

    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            int diff = abs(numbers[i] - numbers[j]);
            if (diff < minDiff) {
                minDiff = diff;
                closest1 = numbers[i];
                closest2 = numbers[j];
            }
        }
    }

    cout << "最接近的两个数字是: " << closest1 << " 和 " << closest2 << endl;

    return 0;
}

效率更高一点算法?

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;

int main() {
    int n;
    cout << "请输入数字的数量: ";
    cin >> n;

    if (n < 2) {
        cout << "至少需要输入两个数字才能找到最接近的两个数字。" << endl;
        return 1;
    }

    vector<int> numbers;
    for (int i = 0; i < n; i++) {
        int num;
        cout << "请输入第 " << i + 1 << " 个数字: ";
        cin >> num;
        numbers.push_back(num);
    }

    sort(numbers.begin(), numbers.end()); // 排序数组

    int minDiff = INT_MAX;
    int closest1, closest2;

    for (int i = 0; i < n - 1; i++) {
        int diff = numbers[i + 1] - numbers[i];
        if (diff < minDiff) {
            minDiff = diff;
            closest1 = numbers[i];
            closest2 = numbers[i + 1];
        }
    }

    cout << "最接近的两个数字是: " << closest1 << " 和 " << closest2 << endl;

    return 0;
}
  1. 统计字符1的个数
#include <iostream>
#include <string>
using namespace std;

int main() {
    string input;
    cout << "请输入一个字符串: ";
    cin >> input;

    int count = 0;

    for (char c : input) {
        if (c == '1') {
            count++;
        }
    }

    cout << "字符 '1' 的个数是: " << count << endl;

    return 0;
}
  1. 前n个整数顺序写在以其,数一数0~9的数字各出现多少次?
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    cout << "请输入一个正整数 n: ";
    cin >> n;

    if (n <= 0) {
        cout << "请输入一个有效的正整数。" << endl;
        return 1;
    }

    vector<int> counts(10, 0); // 初始化一个长度为10,每个元素都为0的向量,用于统计0~9的出现次数

    for (int i = 1; i <= n; i++) {
        int num = i;
        while (num > 0) {
            int digit = num % 10; // 获取最低位的数字
            counts[digit]++; // 增加对应数字的计数
            num /= 10; // 去掉最低位
        }
    }

    // 输出各数字出现次数
    for (int i = 0; i < 10; i++) {
        cout << "数字 " << i << " 出现 " << counts[i] << " 次" << endl;
    }

    return 0;
}

8-es7xztRr0fgvXfT.png

编写一个程序,它将找到所有这些数字,可被7整除,但不是5的倍数,1000至2021(包括在内),输出这些数字并统计满足条件的数字的个数
count = 0  # 用于统计符合条件的数字的数量
result = []  # 存储符合条件的数字

for num in range(1000, 2022):
    if num % 7 == 0 and num % 5 != 0:
        result.append(num)
        count += 1

#print("1000到2021之间可被7整除但不是5的倍数的数字有:")
print(result)
print(f"{count}")
编写一个接受句子并计算字母和数字的程序
sentence = input("请输入一个句子: ")

# 初始化字母和数字的计数器
letter_count = 0
digit_count = 0

# 遍历句子中的每个字符

for char in sentence:
    if char.isalpha():  # 判断字符是否是字母
        letter_count += 1
    elif char.isdigit():  # 判断字符是否是数字
        digit_count += 1

print(f"字母 {letter_count}  数字{digit_count}")
输入一个正整数,输出这个正整数的的阶乘
def factorial(n):
    if n == 0:
        return 1
    else:
        result = 1
        for i in range(1, n + 1):
            result *= i
        return result

try:
    num = int(input("请输入一个正整数: "))
    if num < 0:
        print("请输入一个非负整数。")
    else:
        result = factorial(num)
        print(f"{num} 的阶乘是 {result}")
except ValueError:
    print("请输入一个有效的整数。")
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值的那两个整数,nums指定为:[2,7,11,15],target为用户手动输入。如果nums中找不到两个整数相加的和等于target,则输出不存在
def find_two_sum(nums, target):
    num_indices = {}  # 用于存储数字和它们的索引

    for index, num in enumerate(nums):
        complement = target - num  # 计算与当前数字配对的补数

        if complement in num_indices:
            # 如果补数在字典中,说明找到了一对配对的数字
            return [num_indices[complement], index]

        # 否则,将当前数字添加到字典中
        num_indices[num] = index

    return None  # 如果找不到配对的数字,返回None

# 用户输入目标值
try:
    target = int(input("请输入目标值: "))
    nums = [2, 7, 11, 15]  # 给定的数组
    result = find_two_sum(nums, target)

    if result is not None:
        print(f"和为目标值 {target} 的两个整数是: {nums[result[0]]} 和 {nums[result[1]]}")
    else:
        print("不存在两个整数的和等于目标值。")
except ValueError:
    print("请输入一个有效的整数作为目标值。")
卡拉兹(Callatz)猜想:对任何一个正整数 n,如果它是偶数,那么把它砍掉一半;如果它是奇数,那么把 (3n+1) 砍掉一半。这样一直反复砍下去,最后一定在某一步得到 n=1。
对给定的任一不超过 1000 的正整数 n,简单地数一下,需要多少步(砍几下)才能得到 n=1?
def collatz_steps(n):
    steps = 0  # 初始化步数

    while n != 1:
        if n % 2 == 0:
            n = n // 2
        else:
            n = 3 * n + 1
        steps += 1

    return steps

try:
    n = int(input("请输入一个不超过1000的正整数: "))
    if 1 <= n <= 1000:
        steps_needed = collatz_steps(n)
        print(f"需要 {steps_needed} 步才能得到 n=1。")
    else:
        print("请输入一个介于1和1000之间的正整数。")
except ValueError:
    print("请输入一个有效的整数。")
设计一个函数,输入为两个整数,输出为这两个数的最大公约数
比如,输入:5,15;输出:5
def find_gcd(a, b):
    while b:
        a, b = b, a % b
    return a

# 示例用法
num1 = int(input("请输入第一个整数: "))
num2 = int(input("请输入第二个整数: "))

gcd = find_gcd(num1, num2)
print(f"{num1} 和 {num2} 的最大公约数是 {gcd}")

定义一个函数,输入一个列表,并将列表中的头尾两个元素对调后返回
表,并将列表中的
def swap_first_and_last(lst):
    if len(lst) >= 2:
        lst[0], lst[-1] = lst[-1], lst[0]
    return lst

# 示例用法
input_list = [1, 2, 3, 4, 5]
result = swap_first_and_last(input_list)
print(result)
输入一个正整数n,输出从1到这个正整数n(包括n)的所有正整数的和
def sum_of_integers_up_to_n(n):
    if n < 1:
        return "请输入一个正整数。"
    
    total = 0
    for i in range(1, n + 1):
        total += i
    return total

try:
    n = int(input("请输入一个正整数 n: "))
    result = sum_of_integers_up_to_n(n)
    if type(result) == int:
        print(f"从1到{n}的所有正整数的和是: {result}")
    else:
        print(result)
except ValueError:
    print("请输入一个有效的整数。")
定义一个函数sort_list(),功能为将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。即输入为两个升序列表,输出为合并后的升序列表
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def sort_list(l1, l2):
    # 创建一个哑结点作为新链表的头部
    dummy = ListNode()
    current = dummy  # 用于遍历新链表

    while l1 is not None and l2 is not None:
        if l1.val < l2.val:
            current.next = l1
            l1 = l1.next
        else:
            current.next = l2
            l2 = l2.next
        current = current.next

    # 将剩余的节点连接到新链表
    if l1 is not None:
        current.next = l1
    if l2 is not None:
        current.next = l2

    return dummy.next  # 返回新链表的头部

示例用法

创建两个升序链表

l1 = ListNode(1, ListNode(3, ListNode(5)))
l2 = ListNode(2, ListNode(4, ListNode(6)))

merged_list = sort_list(l1, l2)

# 打印合并后的升序链表
while merged_list:
    print(merged_list.val, end=" ")
    merged_list = merged_list.next
给你一个整数 x ,如果 x 是一个回文整数,输出 true ;否则,输出 false
def is_palindrome(x):
    if x < 0:
        return False

    x_str = str(x)
    return x_str == x_str[::-1]

# 示例用法
num = int(input("请输入一个整数: "))
result = is_palindrome(num)

if result:
    print("是回文整数")
else:
    print("不是回文整数")
猴子偷桃问题:有一只猴子摘了若干个桃子,第一天吃了一半,还不过瘾,又多吃了一个;第二天吃了剩下的一半,还不过瘾,又多吃了一个;以后每天都是吃剩下的一半加一个。编写程序,输入天数和剩下的桃子数,计算猴子摘了多少桃子。
def calculate_starting_peaches(days, peach_left):
    peaches = peach_left

    for day in range(days - 1, 0, -1):
        peaches = (peaches + 1) * 2

    return peaches

try:
    days = int(input("请输入天数: "))
    peach_left = int(input("请输入剩下的桃子数: "))
    
    if days <= 0 or peach_left <= 0:
        print("请输入正整数天数和桃子数。")
    else:
        starting_peaches = calculate_starting_peaches(days, peach_left)
        print(f"一开始有 {starting_peaches} 个桃子。")
except ValueError:
    print("请输入有效的整数。")

0768b74d-6a93-4509-8c5e-e0b32459e9cf.png

题目1

  • 描述
    把前n个数字排列在一起,统计一下0~9各出现多少次? 编写C++程序
  • 输入
    整数n
  • 输出
    0~9 各出现次数
#include <iostream>
#include <vector>

int main() {
    int n;
    std::cout << "请输入数字n: ";
    std::cin >> n;

    // 创建一个数组来存储0到9各数字的出现次数,初始化为0
    int count[10] = {0};

    // 将前n个数字排列在一起
    for (int i = 1; i <= n; i++) {
        int num = i;
        while (num > 0) {
            // 获取最后一位数字
            int digit = num % 10;
            // 增加相应数字的计数
            count[digit]++;
            // 去掉最后一位数字
            num /= 10;
        }
    }

    // 输出统计结果
    std::cout << "0~9各数字出现的次数为:" << std::endl;
    for (int i = 0; i < 10; i++) {
        std::cout << i << ": " << count[i] << std::endl;
    }

    return 0;
}

题目2

  • 描述
    周期串,如果一个字符串可以由某个长度为k的字符串多次重复得到,则称该串以k为周期,输入一个长度不超过80的字符串,输出其最小周期,编写C++程序
  • 输入
    长度不超过80的字符串
  • 输出
    最小周期,一个整数,或者不存在
#include <iostream>
#include <string>

int findSmallestPeriod(const std::string& input) {
    int n = input.length();
    for (int k = 1; k <= n; k++) {
        if (n % k == 0) {
            bool isPeriod = true;
            for (int i = k; i < n; i++) {
                if (input[i] != input[i % k]) {
                    isPeriod = false;
                    break;
                }
            }
            if (isPeriod) {
                return k;
            }
        }
    }
    return n; // 如果没有找到周期,则返回字符串长度
}

int main() {
    std::string input;
    std::cout << "请输入一个字符串(长度不超过80): ";
    std::cin >> input;

    int smallestPeriod = findSmallestPeriod(input);

    std::cout << "最小周期为: " << smallestPeriod << std::endl;

    return 0;
}

解释说明

当我们说一个字符串具有周期性时,意味着该字符串可以由一个较短的子串重复多次组成。例如,字符串 "abcabcabc" 具有周期性,其周期为 "abc",因为它可以重复三次来构建整个字符串。

这个C++程序的目标是找到给定字符串的最小周期。最小周期是能够将字符串重复多次以构建原始字符串的最短子串长度。下面我将逐步解释程序和算法的工作原理:

  • 用户被要求输入一个字符串,该字符串的长度不超过80个字符。
  • findSmallestPeriod 函数是用于查找最小周期的核心部分。它接受一个字符串作为参数,并返回最小周期的长度。
  • 在 findSmallestPeriod 函数中,我们首先获取输入字符串的长度并将其存储在变量 n 中。
  • 接下来,我们使用一个循环来尝试不同的周期长度 k,从1到字符串的长度 n。对于每个 k 值,我们检查是否可以通过重复长度为 k 的子串来构建原始字符串。
  • 在循环中,我们首先检查 n 是否可以被 k 整除,因为只有在整除的情况下才可能存在周期性。
  • 如果 n 可以被 k 整除,那么我们进一步检查是否可以通过重复 k 长度的子串来构建原始字符串。我们使用一个嵌套循环遍历字符串的每个字符,检查是否与其对应的周期位置上的字符相同。
  • 如果所有的字符都匹配,那么我们就找到了一个周期,我们返回 k 作为最小周期的长度。
  • 如果没有找到周期,我们继续尝试下一个 k 值,直到遍历所有可能的周期长度。
  • 如果在整个循环中都没有找到周期,那么我们默认将字符串的长度 n 作为最小周期长度。
  • 最后,程序输出找到的最小周期长度。

这个算法的时间复杂度是比较高的,因为它需要尝试多个周期长度。但是,对于较短的字符串,它仍然是一个合理的方法。如果字符串很长,可能需要考虑更高效的算法。这个程序提供了一个基本的示例,演示了如何找到字符串的最小周期。

时间复杂度:当我们谈论一个算法的时间复杂度时,实际上是在讨论这个算法需要多长时间来完成任务。可以将时间复杂度想象成一个度量标准,用来衡量不同算法在处理相同问题时,谁更快或更慢。
想象一下你和你的朋友在做同样的数学题,但你们使用了不同的方法。时间复杂度就像是一种方式,帮助你们比较谁的方法更快。
时间复杂度告诉我们,当输入的数据规模增加时,算法执行所需的时间会如何增加。一个具有较低时间复杂度的算法通常会更快,因为它可以在更短的时间内处理更多的数据。
举个例子,假设你和你的朋友都要计算从1加到100的和。你选择了直接相加的方法,而你的朋友选择了使用一个数学公式(如等差数列求和公式)。尽管两种方法都可以得到正确的答案,但使用公式的方法更快,因此它的时间复杂度更低。
所以,时间复杂度就是一种衡量算法执行速度的方式,它帮助我们选择最合适的算法来解决问题,特别是在处理大量数据时,能够帮助我们选择更高效的方法。

OIG.puUcNNdAsG.jpeg

之前使用python里面的pygame可以很容易构建snake游戏,其实pygame后端也是封装了sdl库,所以pygame编写的snake,使用C++
SDL2编写应该很容易移植过来

不过我不想自己亲自编写,我想偷懒让人工智能帮我完成看看,结果表明要一次达成目标似乎也不是那么顺利
经过几次对话,好歹勉强实现了贪吃蛇的基础功能

第一次尝试
第一次对话
代码

#include <SDL.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

using namespace std;

// 定义游戏窗口的宽度和高度
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;

// 定义Snake的单元格大小
const int CELL_SIZE = 20;

// 定义Snake的初始长度
const int INITIAL_LENGTH = 5;

// 定义Snake的方向
enum class Direction { UP, DOWN, LEFT, RIGHT };

// 定义Snake的单元格结构体
struct Cell {
    int x, y;
};

// Snake类
class Snake {
public:
    Snake();
    void HandleInput(SDL_Event &e);
    void Move();
    bool CheckCollision();
    void Grow();
    void Render(SDL_Renderer* renderer);
private:
    vector<Cell> body;
    Direction direction;
};

Snake::Snake() {
    // 初始化Snake的位置和方向
    for (int i = 0; i < INITIAL_LENGTH; ++i) {
        body.push_back({SCREEN_WIDTH / 2 - i * CELL_SIZE, SCREEN_HEIGHT / 2});
    }
    direction = Direction::RIGHT;
}

void Snake::HandleInput(SDL_Event &e) {
    // 处理用户输入,改变Snake的方向
    if (e.type == SDL_KEYDOWN) {
        switch (e.key.keysym.sym) {
            case SDLK_UP:
                if (direction != Direction::DOWN)
                    direction = Direction::UP;
                break;
            case SDLK_DOWN:
                if (direction != Direction::UP)
                    direction = Direction::DOWN;
                break;
            case SDLK_LEFT:
                if (direction != Direction::RIGHT)
                    direction = Direction::LEFT;
                break;
            case SDLK_RIGHT:
                if (direction != Direction::LEFT)
                    direction = Direction::RIGHT;
                break;
        }
    }
}

void Snake::Move() {
    // 移动Snake的头部
    int newHeadX = body.front().x;
    int newHeadY = body.front().y;

    switch (direction) {
        case Direction::UP:
            newHeadY -= CELL_SIZE;
            break;
        case Direction::DOWN:
            newHeadY += CELL_SIZE;
            break;
        case Direction::LEFT:
            newHeadX -= CELL_SIZE;
            break;
        case Direction::RIGHT:
            newHeadX += CELL_SIZE;
            break;
    }

    // 添加新的头部
    body.insert(body.begin(), {newHeadX, newHeadY});
}

bool Snake::CheckCollision() {
    // 检查是否与自身相撞
    for (size_t i = 1; i < body.size(); ++i) {
        if (body[i].x == body.front().x && body[i].y == body.front().y) {
            return true;
        }
    }

    // 检查是否超出屏幕边界
    if (body.front().x < 0 || body.front().x >= SCREEN_WIDTH || body.front().y < 0 || body.front().y >= SCREEN_HEIGHT) {
        return true;
    }

    return false;
}

void Snake::Grow() {
    // Snake吃到食物时增加长度
    int tailX = body.back().x;
    int tailY = body.back().y;

    switch (direction) {
        case Direction::UP:
            tailY += CELL_SIZE;
            break;
        case Direction::DOWN:
            tailY -= CELL_SIZE;
            break;
        case Direction::LEFT:
            tailX += CELL_SIZE;
            break;
        case Direction::RIGHT:
            tailX -= CELL_SIZE;
            break;
    }

    body.push_back({tailX, tailY});
}

void Snake::Render(SDL_Renderer* renderer) {
    // 渲染Snake
    for (const Cell& cell : body) {
        SDL_Rect rect = {cell.x, cell.y, CELL_SIZE, CELL_SIZE};
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Snake颜色
        SDL_RenderFillRect(renderer, &rect);
    }
}

// 主程序
int main(int argc, char* args[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    srand(time(nullptr));

    Snake snake;

    // 游戏主循环
    bool quit = false;
    SDL_Event e;
    while (!quit) {
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
            snake.HandleInput(e);
        }

        // 移动Snake
        snake.Move();

        // 检查碰撞
        if (snake.CheckCollision()) {
            quit = true;
        }

        // 渲染
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // 清空屏幕
        SDL_RenderClear(renderer);
        snake.Render(renderer);
        SDL_RenderPresent(renderer);

        // 控制游戏速度
        SDL_Delay(100);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

运行结果,看起来不符合预期
第一次运行结果

第二次尝试
第二次尝试
代码

#include <SDL.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

using namespace std;

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int CELL_SIZE = 20;

enum class Direction { UP, DOWN, LEFT, RIGHT };

struct Cell {
    int x, y;
};

class Snake {
public:
    Snake();
    void HandleInput(SDL_Event &e);
    void Move();
    bool CheckCollision();
    void Grow();
    void Render(SDL_Renderer* renderer);
private:
    vector<Cell> body;
    Direction direction;
};

Snake::Snake() {
    body.push_back({SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2});
    direction = Direction::RIGHT;
}

void Snake::HandleInput(SDL_Event &e) {
    if (e.type == SDL_KEYDOWN) {
        switch (e.key.keysym.sym) {
            case SDLK_UP:
                if (direction != Direction::DOWN)
                    direction = Direction::UP;
                break;
            case SDLK_DOWN:
                if (direction != Direction::UP)
                    direction = Direction::DOWN;
                break;
            case SDLK_LEFT:
                if (direction != Direction::RIGHT)
                    direction = Direction::LEFT;
                break;
            case SDLK_RIGHT:
                if (direction != Direction::LEFT)
                    direction = Direction::RIGHT;
                break;
        }
    }
}

void Snake::Move() {
    int newHeadX = body.front().x;
    int newHeadY = body.front().y;

    switch (direction) {
        case Direction::UP:
            newHeadY -= CELL_SIZE;
            break;
        case Direction::DOWN:
            newHeadY += CELL_SIZE;
            break;
        case Direction::LEFT:
            newHeadX -= CELL_SIZE;
            break;
        case Direction::RIGHT:
            newHeadX += CELL_SIZE;
            break;
    }

    body.insert(body.begin(), {newHeadX, newHeadY});
}

bool Snake::CheckCollision() {
    for (size_t i = 1; i < body.size(); ++i) {
        if (body[i].x == body.front().x && body[i].y == body.front().y) {
            return true;
        }
    }

    if (body.front().x < 0 || body.front().x >= SCREEN_WIDTH || body.front().y < 0 || body.front().y >= SCREEN_HEIGHT) {
        return true;
    }

    return false;
}

void Snake::Grow() {
    int tailX = body.back().x;
    int tailY = body.back().y;

    switch (direction) {
        case Direction::UP:
            tailY += CELL_SIZE;
            break;
        case Direction::DOWN:
            tailY -= CELL_SIZE;
            break;
        case Direction::LEFT:
            tailX += CELL_SIZE;
            break;
        case Direction::RIGHT:
            tailX -= CELL_SIZE;
            break;
    }

    body.push_back({tailX, tailY});
}

void Snake::Render(SDL_Renderer* renderer) {
    for (size_t i = 0; i < body.size(); ++i) {
        SDL_Rect rect = {body[i].x, body[i].y, CELL_SIZE, CELL_SIZE};
        if (i == 0) {
            SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); // Snake头部为黄色
        } else {
            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Snake身体为绿色
        }
        SDL_RenderFillRect(renderer, &rect);
    }
}

int main(int argc, char* args[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    srand(time(nullptr));

    Snake snake;

    bool quit = false;
    SDL_Event e;
    while (!quit) {
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
            snake.HandleInput(e);
        }

        snake.Move();

        if (snake.CheckCollision()) {
            quit = true;
        }

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        snake.Render(renderer);
        SDL_RenderPresent(renderer);

        SDL_Delay(100);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

结果貌似还是不符合预期
截图 2023-09-23 12-38-08.png

第三次尝试
截图 2023-09-23 12-40-39.png

代码

#include <SDL.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

using namespace std;

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int CELL_SIZE = 20;

enum class Direction { UP, DOWN, LEFT, RIGHT };

struct Cell {
    int x, y;
};

class Snake {
public:
    Snake();
    void HandleInput(SDL_Event &e);
    void Move();
    bool CheckCollision();
    void Grow();
    void Render(SDL_Renderer* renderer);
    Cell GetHeadPosition();
private:
    vector<Cell> body;
    Direction direction;
};

Snake::Snake() {
    body.push_back({SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2});
    direction = Direction::RIGHT;
}

void Snake::HandleInput(SDL_Event &e) {
    if (e.type == SDL_KEYDOWN) {
        switch (e.key.keysym.sym) {
            case SDLK_UP:
                if (direction != Direction::DOWN)
                    direction = Direction::UP;
                break;
            case SDLK_DOWN:
                if (direction != Direction::UP)
                    direction = Direction::DOWN;
                break;
            case SDLK_LEFT:
                if (direction != Direction::RIGHT)
                    direction = Direction::LEFT;
                break;
            case SDLK_RIGHT:
                if (direction != Direction::LEFT)
                    direction = Direction::RIGHT;
                break;
        }
    }
}

void Snake::Move() {
    int newHeadX = body.front().x;
    int newHeadY = body.front().y;

    switch (direction) {
        case Direction::UP:
            newHeadY -= CELL_SIZE;
            break;
        case Direction::DOWN:
            newHeadY += CELL_SIZE;
            break;
        case Direction::LEFT:
            newHeadX -= CELL_SIZE;
            break;
        case Direction::RIGHT:
            newHeadX += CELL_SIZE;
            break;
    }

    body.insert(body.begin(), {newHeadX, newHeadY});
}

bool Snake::CheckCollision() {
    for (size_t i = 1; i < body.size(); ++i) {
        if (body[i].x == body.front().x && body[i].y == body.front().y) {
            return true;
        }
    }

    if (body.front().x < 0 || body.front().x >= SCREEN_WIDTH || body.front().y < 0 || body.front().y >= SCREEN_HEIGHT) {
        return true;
    }

    return false;
}

void Snake::Grow() {
    // 在Snake的尾部增加一个单元格
    int tailX = body.back().x;
    int tailY = body.back().y;

    switch (direction) {
        case Direction::UP:
            tailY += CELL_SIZE;
            break;
        case Direction::DOWN:
            tailY -= CELL_SIZE;
            break;
        case Direction::LEFT:
            tailX += CELL_SIZE;
            break;
        case Direction::RIGHT:
            tailX -= CELL_SIZE;
            break;
    }

    body.push_back({tailX, tailY});
}

void Snake::Render(SDL_Renderer* renderer) {
    for (size_t i = 0; i < body.size(); ++i) {
        SDL_Rect rect = {body[i].x, body[i].y, CELL_SIZE, CELL_SIZE};
        if (i == 0) {
            SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); // Snake头部为黄色
        } else {
            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Snake身体为绿色
        }
        SDL_RenderFillRect(renderer, &rect);
    }
}

Cell Snake::GetHeadPosition() {
    return body.front();
}

int main(int argc, char* args[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    srand(time(nullptr));

    Snake snake;

    // 初始化食物位置
    Cell food = {rand() % (SCREEN_WIDTH / CELL_SIZE) * CELL_SIZE, rand() % (SCREEN_HEIGHT / CELL_SIZE) * CELL_SIZE};

    bool quit = false;
    SDL_Event e;
    while (!quit) {
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
            snake.HandleInput(e);
        }

        snake.Move();

        if (snake.GetHeadPosition().x == food.x && snake.GetHeadPosition().y == food.y) {
            // Snake吃到食物时,增加长度并生成新的食物位置
            snake.Grow();
            food = {rand() % (SCREEN_WIDTH / CELL_SIZE) * CELL_SIZE, rand() % (SCREEN_HEIGHT / CELL_SIZE) * CELL_SIZE};
        }

        if (snake.CheckCollision()) {
            quit = true;
        }

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        snake.Render(renderer);

        // 渲染食物
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // 食物为绿色
        SDL_Rect foodRect = {food.x, food.y, CELL_SIZE, CELL_SIZE};
        SDL_RenderFillRect(renderer, &foodRect);

        SDL_RenderPresent(renderer);

        SDL_Delay(100);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

运行结果,依然不符合预期,真是糟糕
截图 2023-09-23 12-42-20.png

第四次尝试,看看
截图 2023-09-23 12-42-55.png

代码

#include <SDL.h>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

using namespace std;

const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int CELL_SIZE = 20;

enum class Direction { UP, DOWN, LEFT, RIGHT };

struct Cell {
    int x, y;
};

class Snake {
public:
    Snake();
    void HandleInput(SDL_Event &e);
    void Move();
    bool CheckCollision();
    void Grow();
    void Render(SDL_Renderer* renderer);
    Cell GetHeadPosition();
private:
    vector<Cell> body;
    Direction direction;
    bool growing;  // 用于标记是否吃到食物后需要增长
};

Snake::Snake() {
    body.push_back({SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2});
    direction = Direction::RIGHT;
    growing = false;  // 初始化时不需要增长
}

void Snake::HandleInput(SDL_Event &e) {
    if (e.type == SDL_KEYDOWN) {
        switch (e.key.keysym.sym) {
            case SDLK_UP:
                if (direction != Direction::DOWN)
                    direction = Direction::UP;
                break;
            case SDLK_DOWN:
                if (direction != Direction::UP)
                    direction = Direction::DOWN;
                break;
            case SDLK_LEFT:
                if (direction != Direction::RIGHT)
                    direction = Direction::LEFT;
                break;
            case SDLK_RIGHT:
                if (direction != Direction::LEFT)
                    direction = Direction::RIGHT;
                break;
        }
    }
}

void Snake::Move() {
    int newHeadX = body.front().x;
    int newHeadY = body.front().y;

    switch (direction) {
        case Direction::UP:
            newHeadY -= CELL_SIZE;
            break;
        case Direction::DOWN:
            newHeadY += CELL_SIZE;
            break;
        case Direction::LEFT:
            newHeadX -= CELL_SIZE;
            break;
        case Direction::RIGHT:
            newHeadX += CELL_SIZE;
            break;
    }

    body.insert(body.begin(), {newHeadX, newHeadY});

    // 如果吃到食物,则不删除尾部,实现增长
    if (!growing) {
        body.pop_back();
    } else {
        growing = false;
    }
}

bool Snake::CheckCollision() {
    for (size_t i = 1; i < body.size(); ++i) {
        if (body[i].x == body.front().x && body[i].y == body.front().y) {
            return true;
        }
    }

    if (body.front().x < 0 || body.front().x >= SCREEN_WIDTH || body.front().y < 0 || body.front().y >= SCREEN_HEIGHT) {
        return true;
    }

    return false;
}

void Snake::Grow() {
    // 在Snake的尾部增加一个单元格
    int tailX = body.back().x;
    int tailY = body.back().y;

    switch (direction) {
        case Direction::UP:
            tailY += CELL_SIZE;
            break;
        case Direction::DOWN:
            tailY -= CELL_SIZE;
            break;
        case Direction::LEFT:
            tailX += CELL_SIZE;
            break;
        case Direction::RIGHT:
            tailX -= CELL_SIZE;
            break;
    }

    body.push_back({tailX, tailY});
    growing = true;  // 标记为需要增长
}

void Snake::Render(SDL_Renderer* renderer) {
    for (size_t i = 0; i < body.size(); ++i) {
        SDL_Rect rect = {body[i].x, body[i].y, CELL_SIZE, CELL_SIZE};
        if (i == 0) {
            SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); // Snake头部为黄色
        } else {
            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Snake身体为绿色
        }
        SDL_RenderFillRect(renderer, &rect);
    }
}

Cell Snake::GetHeadPosition() {
    return body.front();
}

int main(int argc, char* args[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("Snake Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    srand(time(nullptr));

    Snake snake;

    // 初始化食物位置
    Cell food = {rand() % (SCREEN_WIDTH / CELL_SIZE) * CELL_SIZE, rand() % (SCREEN_HEIGHT / CELL_SIZE) * CELL_SIZE};

    bool quit = false;
    SDL_Event e;
    while (!quit) {
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
            snake.HandleInput(e);
        }

        snake.Move();

        if (snake.GetHeadPosition().x == food.x && snake.GetHeadPosition().y == food.y) {
            // Snake吃到食物时,增加长度并生成新的食物位置
            snake.Grow();
            food = {rand() % (SCREEN_WIDTH / CELL_SIZE) * CELL_SIZE, rand() % (SCREEN_HEIGHT / CELL_SIZE) * CELL_SIZE};
        }

        if (snake.CheckCollision()) {
            quit = true;
        }

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        snake.Render(renderer);

        // 渲染食物
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // 食物为绿色
        SDL_Rect foodRect = {food.x, food.y, CELL_SIZE, CELL_SIZE};
        SDL_RenderFillRect(renderer, &foodRect);

        SDL_RenderPresent(renderer);

        SDL_Delay(100);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

运行结果,看起来达到预期
截图 2023-09-23 12-44-48.png

透过这几次对话,我们可以看到要想快速达到预期的结果,提问非常关键,如果一开始就给出比较细致的说明,应该能够一次成功,需要试试!

透过比较几次代码的差异,也可以从中学习到程序bug产生的原因

8-W5W9Tnz1rQ5URXa.png

新的编程工具使用,总是需要环境搭建

IDE + 库

SDL2_mixer-devel-2.6.3-mingw.zip

SDL2 Hello World版本

#include <SDL.h>
#include <iostream>
#include <SDL_ttf.h>

int main(int argc, char* argv[])
{
    // 初始化SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        std::cerr << "无法初始化SDL: " << SDL_GetError() << std::endl;
        return 1;
    }

    // 创建窗口
    SDL_Window* window = SDL_CreateWindow("Hello SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
    if (!window) {
        std::cerr << "无法创建窗口: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }

    // 创建渲染器
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        std::cerr << "无法创建渲染器: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    if (TTF_Init() == -1) {
        std::cerr << "无法初始化TTF库: " << TTF_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }

    TTF_Font* font = TTF_OpenFont("Pacifico.ttf", 36);
    if (!font) {
        std::cerr << "无法加载字体: " << TTF_GetError() << std::endl;
        TTF_Quit();
        SDL_Quit();
        return 1;
    }




    // 渲染一些文本
    SDL_Surface* surface = SDL_GetWindowSurface(window);
    SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 255, 255));
    SDL_Color textColor = {255, 255, 255};
    SDL_Surface* textSurface = TTF_RenderText_Solid(font, "Hello, SDL!", textColor);
    SDL_Rect textRect = {200, 200, textSurface->w, textSurface->h};

    SDL_Texture* textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
    SDL_RenderCopy(renderer, textTexture, NULL, &textRect);

    SDL_RenderPresent(renderer);

    // 处理事件循环
    bool quit = false;
    SDL_Event event;
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                quit = true;
            }
        }
    }

    // 清理资源
    TTF_CloseFont(font);
    TTF_Quit();

    SDL_DestroyTexture(textTexture);
    SDL_FreeSurface(textSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

hello world详细解释

#include <SDL.h>
#include <iostream>

这两行是包含头文件的指令。第一行包含了SDL2的头文件,它是用于与SDL2库交互的必要头文件。第二行包含了C++标准库的头文件iostream,它用于在控制台输出错误信息。

int main(int argc, char* argv[]) {

这是C++程序的入口点。main 函数是程序运行的起始点。int argc 和 char* argv[] 用于传递命令行参数。

    // 初始化SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        std::cerr << "无法初始化SDL: " << SDL_GetError() << std::endl;
        return 1;
    }

这一部分初始化了SDL2库。SDL_Init(SDL_INIT_VIDEO) 函数初始化SDL2的视频子系统。如果初始化失败,会输出错误信息并返回1表示程序出错。

    // 创建窗口
    SDL_Window* window = SDL_CreateWindow("Hello SDL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
    if (!window) {
        std::cerr << "无法创建窗口: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }

这一部分创建了一个窗口。SDL_CreateWindow 函数用于创建一个窗口,并指定窗口的标题、位置和大小。如果创建失败,会输出错误信息,然后清理SDL2并返回1。

    // 创建渲染器
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        std::cerr << "无法创建渲染器: " << SDL_GetError() << std::endl;
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

这一部分创建了一个渲染器,用于在窗口上绘制图形。SDL_CreateRenderer 函数将渲染器与窗口关联起来。如果创建失败,会输出错误信息,然后清理窗口和SDL2,并返回1。

    // 渲染一些文本
    SDL_Surface* surface = SDL_GetWindowSurface(window);
    SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 255, 255));

这部分代码获取窗口表面并用白色填充整个窗口,以准备绘制文本。

    SDL_Color textColor = {0, 0, 0};
    SDL_Surface* textSurface = TTF_RenderText_Solid(font, "Hello, SDL!", textColor);
    SDL_Rect textRect = {200, 200, textSurface->w, textSurface->h};

这一部分定义了文本的颜色,并创建了一个包含文本的表面(textSurface)。你需要先定义一个字体(font),这里并没有包含在示例中。

    SDL_Texture* textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
    SDL_RenderCopy(renderer, textTexture, NULL, &textRect);
    SDL_RenderPresent(renderer);

这一部分将文本表面转换为纹理,并使用渲染器将文本绘制到窗口上,最后调用SDL_RenderPresent函数来更新窗口显示。

    // 处理事件循环
    bool quit = false;
    SDL_Event event;
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                quit = true;
            }
        }
    }

这一部分处理SDL2的事件循环。它会一直运行,直到收到退出事件(如关闭窗口按钮)。

    // 清理资源
    SDL_DestroyTexture(textTexture);
    SDL_FreeSurface(textSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

最后,这部分清理了所有分配的资源,包括纹理、表面、渲染器、窗口,并调用SDL_Quit来关闭SDL2库。最后,返回0表示程序成功结束。

上面的代码bug

仔细看我们并没有定义font?

解决方法:

  • 首先,你需要在代码中包含SDL2_ttf头文件:

    #include <SDL_ttf.h>
  • 然后,在主函数的开头,初始化TTF库:

    if (TTF_Init() == -1) {
      std::cerr << "无法初始化TTF库: " << TTF_GetError() << std::endl;
      SDL_Quit();
      return 1;
    }
  • 接下来,你需要加载一个TrueType字体文件,例如:

    TTF_Font* font = TTF_OpenFont("path/to/your/font.ttf", 36);
    if (!font) {
      std::cerr << "无法加载字体: " << TTF_GetError() << std::endl;
      TTF_Quit();
      SDL_Quit();
      return 1;
    }

    请确保将 "path/to/your/font.ttf" 替换为你自己字体文件的路径。

最后,在绘制文本部分,使用这个字体来渲染文本:

SDL_Surface* textSurface = TTF_RenderText_Solid(font, "Hello, SDL!", textColor);

这样,你就可以使用指定的字体来渲染文本了。请不要忘记在程序结束时清理字体资源:

TTF_CloseFont(font);
TTF_Quit();

例子工程代码: