Day 7 字符串和常用数据结构

字符串和常用数据结构

字符串

字符串是计算机编程中表示文本数据的数据类型。在大多数编程语言中,字符串由一系列字符组成,可以是字母、数字、符号和空格等字符的组合。在Python中,字符串用引号(单引号或双引号)括起来表示。

字符串在编程中非常常见,用于存储和操作文本信息。它们可以包含任意长度的字符序列,并且可以进行许多常见的操作,如连接、分割、替换、查找等。

在Python中,字符串是不可变的,这意味着一旦创建了字符串,就不能修改它的内容。当对字符串进行操作时,实际上是创建了一个新的字符串对象。例如,连接两个字符串会创建一个新的字符串,原始字符串不会被修改。

以下是一些示例字符串的声明和使用方法:

# 声明字符串
str1 = "Hello, World!"  # 使用双引号声明字符串
str2 = 'This is a string.'  # 使用单引号声明字符串

# 打印字符串
print(str1)  # 输出:Hello, World!
print(str2)  # 输出:This is a string.

# 字符串连接
str3 = str1 + " " + str2
print(str3)  # 输出:Hello, World! This is a string.

# 字符串长度
length = len(str1)
print(length)  # 输出:13

# 字符串索引
char = str1[0]
print(char)  # 输出:H

# 切片操作
substring = str1[7:12]
print(substring)  # 输出:World

# 字符串转换为大写或小写
upper_str = str1.upper()
lower_str = str2.lower()
print(upper_str)  # 输出:HELLO, WORLD!
print(lower_str)  # 输出:this is a string.

# 字符串查找
index = str1.index("World")
print(index)  # 输出:7

# 字符串替换
new_str = str1.replace("World", "Python")
print(new_str)  # 输出:Hello, Python!

字符串转义

字符串转义是指在字符串中使用特殊的字符序列来表示一些特殊的字符或符号。这些特殊字符序列以反斜杠(\)开头,并紧跟着一个或多个字符,用于表示特定的含义。

下面是一些常见的转义字符及其含义:

':单引号
":双引号
\:反斜杠
\n:换行符
\t:制表符
\r:回车符
\b:退格符
\f:换页符
通过使用转义字符,你可以在字符串中插入特殊字符,而不会被解释为字符串的一部分。下面是一些示例:

# 使用转义字符
str1 = 'This is a single-quoted string with a \'single quote\' inside.'
str2 = "This is a double-quoted string with a \"double quote\" inside."
str3 = "This string has a newline character:\nIt will create a new line."
str4 = "This string has a tab character:\tIt will create a tab space."

print(str1)  # 输出:This is a single-quoted string with a 'single quote' inside.
print(str2)  # 输出:This is a double-quoted string with a "double quote" inside.
print(str3)  # 输出:This string has a newline character:
              #      It will create a new line.
print(str4)  # 输出:This string has a tab character:    It will create a tab space.

需要注意的是,在使用转义字符时,反斜杠本身也需要进行转义,即使用双反斜杠(\)来表示一个反斜杠字符。这是因为反斜杠在字符串中有特殊的含义,所以需要使用另一个反斜杠来转义它。

另外,还有一种方式是使用原始字符串(Raw String),在字符串前添加一个小写字母r或大写字母R来表示。原始字符串中的转义字符将被视为普通字符,而不进行转义。例如:

raw_str = r"This is a raw string with a \n newline character."
print(raw_str)  # 输出:This is a raw string with a \n newline character.

字符串切片

字符串切片是指从一个字符串中获取部分子字符串的操作。通过指定起始索引和结束索引,可以从字符串中提取出指定范围的子字符串。
字符串切片的语法通常是string[start:end],其中start表示起始索引(包含),end表示结束索引(不包含)。切片操作将返回从起始索引到结束索引之间的子字符串。

str1 = "Hello, World!"

# 提取部分子字符串
substring1 = str1[7:12]
print(substring1)  # 输出:World

substring2 = str1[0:5]
print(substring2)  # 输出:Hello

substring3 = str1[7:]
print(substring3)  # 输出:World!

substring4 = str1[:5]
print(substring4)  # 输出:Hello

# 使用负数索引
substring5 = str1[-6:-1]
print(substring5)  # 输出:World

# 步长切片
substring6 = str1[::2]
print(substring6)  # 输出:HloWrd

substring7 = str1[::-1]
print(substring7)  # 输出:!dlroW ,olleH

字符串处理

连接字符串:将两个或多个字符串合并成一个字符串。在大多数编程语言中,可以使用+运算符或字符串拼接函数来实现字符串的连接。

str1 = "Hello"
str2 = "World"
result = str1 + ", " + str2
print(result)  # 输出:Hello, World

字符串长度:获取字符串中字符的数量。通常使用len()函数来计算字符串的长度。

str1 = "Hello, World!"
length = len(str1)
print(length)  # 输出:13

字符串查找和替换:在字符串中查找特定的子字符串,并进行替换。可以使用find()或index()函数来查找子字符串的位置,并使用replace()函数来替换子字符串。

str1 = "Hello, World!"
index = str1.index("World")
print(index)  # 输出:7

new_str = str1.replace("World", "Python")
print(new_str)  # 输出:Hello, Python!

字符串分割:将字符串分割成多个部分,通常以特定的分隔符为准。可以使用split()函数来实现字符串的分割。

str1 = "Hello, World!"
parts = str1.split(", ")
print(parts)  # 输出:['Hello', 'World!']

字符串大小写转换:将字符串转换为大写或小写形式。可以使用upper()函数将字符串转换为大写,使用lower()函数将字符串转换为小写。

str1 = "Hello, World!"
upper_str = str1.upper()
lower_str = str1.lower()
print(upper_str)  # 输出:HELLO, WORLD!
print(lower_str)  # 输出:hello, world!

字符串格式化:将变量的值插入到字符串中的占位符位置。在Python中,可以使用字符串的format()方法或者f-strings(格式化字符串字面值)来实现字符串的格式化。

name = "Alice"
age = 25
message = "My name is {} and I'm {} years old.".format(name, age)
print(message)  # 输出:My name is Alice and I'm 25 years old.

message = f"My name is {name} and I'm {age} years old."
print(message)  # 输出:My name is Alice and I'm 25 years old.

列表

列表用方括号[]表示,各个元素之间用逗号分隔。下面是一个简单的列表示例:

fruits = ["apple", "banana", "orange", "grape"]

列表的特点包括:

有序性:列表中的元素按照插入的顺序排列,并且可以根据索引访问和操作各个元素。

可变性:列表是可变的,意味着可以修改、添加或删除列表中的元素。

下面介绍列表的操作方法:
定义列表:使用方括号[]来定义一个列表,并将元素以逗号分隔放在方括号中。

fruits = ["apple", "banana", "orange", "grape"]

遍历列表:可以使用循环结构(如for循环)来遍历列表中的元素。

fruits = ["apple", "banana", "orange", "grape"]
for fruit in fruits:
    print(fruit)
# 输出:
# apple
# banana
# orange
# grape

列表的下标运算:可以使用索引来访问列表中的元素。列表的索引从0开始,可以使用正向索引和反向索引。

fruits = ["apple", "banana", "orange", "grape"]
print(fruits[0])    # 输出:apple
print(fruits[-1])   # 输出:grape

向列表中添加元素:有多种方法可以向列表中添加元素。可以使用append()方法在列表末尾添加新的元素,或使用insert()方法在指定位置插入元素。

fruits = ["apple", "banana", "orange", "grape"]
fruits.append("mango")
print(fruits)  # 输出:["apple", "banana", "orange", "grape", "mango"]

fruits.insert(2, "pineapple")
print(fruits)  # 输出:["apple", "banana", "pineapple", "orange", "grape", "mango"]

从列表中移除元素:可以使用del关键字、remove()方法或pop()方法从列表中移除元素。

fruits = ["apple", "banana", "orange", "grape"]
del fruits[1]
print(fruits)  # 输出:["apple", "orange", "grape"]

fruits.remove("orange")
print(fruits)  # 输出:["apple", "grape"]

removed_fruit = fruits.pop(0)
print(removed_fruit)  # 输出:apple
print(fruits)        # 输出:["grape"]

列表的切片操作:可以使用切片操作来获取列表的子列表。

fruits = ["apple", "banana", "orange", "grape"]
sub_list = fruits[1:3]
print(sub_list)  # 输出:["banana", "orange"]

对列表的排序操作:可以使用sort()方法对列表进行排序。默认情况下,sort()方法会按照升序对列表进行排序。如果要按照降序排序,可以将reverse参数设置为True。

numbers = [5, 2, 8, 1, 9]
numbers.sort()
print(numbers)  # 输出:[1, 2, 5, 8, 9]

numbers.sort(reverse=True)
print(numbers)  # 输出:[9, 8, 5, 2, 1]

生成式和生成器

生成式(Comprehension)和生成器(Generator)是 Python 中用于创建可迭代对象的功能。

生成式:生成式是一种用于创建列表、集合或字典的简洁方法。它使用一种紧凑的语法来描述元素的生成规则,并自动将生成的元素组合成所需的数据结构。

··列表生成式(List Comprehension):使用方括号[]来创建一个列表,其中包含一个表达式和一个可选的过滤条件。

numbers = [x for x in range(1, 6)]  # 创建一个包含1到5的列表
even_numbers = [x for x in range(1, 11) if x % 2 == 0]  # 创建一个包含1到10之间的偶数的列表

··集合生成式(Set Comprehension):使用花括号{}来创建一个集合,其中包含一个表达式和一个可选的过滤条件。

squares = {x ** 2 for x in range(1, 6)}  # 创建一个包含1到5的平方的集合

··字典生成式(Dictionary Comprehension):使用花括号{}来创建一个字典,其中包含一个键值对的表达式和一个可选的过滤条件。

numbers = {x: x ** 2 for x in range(1, 6)}  # 创建一个包含1到5的数及其平方的字典

生成器:生成器是一种用于惰性地生成数据的对象。与列表一次性生成所有元素不同,生成器一次只生成一个元素,并在需要时逐个生成。生成器使用函数和yield关键字来定义,可以通过next()函数或迭代器来访问生成器的元素。

def square_numbers(n):
    for i in range(1, n + 1):
        yield i ** 2

squares = square_numbers(5)  # 创建一个生成器对象
print(next(squares))  # 输出:1
print(next(squares))  # 输出:4
print(next(squares))  # 输出:9
print(next(squares))  # 输出:16
print(next(squares))  # 输出:25
‘’‘
这里的`square_numbers()`函数定义了一个生成器,它按照给定范围内的数的平方逐个生成元素。通过调用`next()`函数来访问生成器的元素,直到生成器没有更多的元素可生成时会引发`StopIteration`异常。
’‘’

元组

元组(Tuple)是Python中的一种不可变序列类型,类似于列表,但元组的元素不能被修改。元组使用圆括号()来定义,并使用逗号,来分隔元素。
元组的常见操作:

my_tuple = (1, 2, 3, "apple", "banana")

# 访问元素
print(my_tuple[0])  # 输出:1
print(my_tuple[3])  # 输出:"apple"

# 元组的切片操作
sub_tuple = my_tuple[1:4]
print(sub_tuple)  # 输出:(2, 3, "apple")

# 获取长度
print(len(my_tuple))  # 输出:5

# 连接元组
tuple1 = (1, 2, 3)
tuple2 = ("apple", "banana")
new_tuple = tuple1 + tuple2
print(new_tuple)  # 输出:(1, 2, 3, "apple", "banana")

# 重复元组元素
repeated_tuple = my_tuple * 3
print(repeated_tuple)  # 输出:(1, 2, 3, "apple", "banana", 1, 2, 3, "apple", "banana", 1, 2, 3, "apple", "banana")

# 判断成员
print(2 in my_tuple)  # 输出:True
print("orange" not in my_tuple)  # 输出:True

# 元组解包
a, b, c, d, e = my_tuple
print(a, b, c, d, e)  # 输出:1 2 3 "apple" "banana"

由于元组是不可变的,因此不能直接修改元组的元素。如果需要对元组进行修改,可以通过创建一个新的元组来实现所需的更改。元组通常用于存储不可变的数据集合,或者在需要确保数据不被修改时使用。

集合

集合(Set)是Python中的一种无序、可变的数据类型,它是由唯一且不可变的元素组成。集合中的元素不能重复,且没有固定的顺序。集合使用大括号{}来定义,并使用逗号,来分隔元素。

# 定义集合
my_set = {1, 2, 3, 4, 5}

# 访问集合元素
for element in my_set:
    print(element)

# 添加元素
my_set.add(6)
print(my_set)  # 输出:{1, 2, 3, 4, 5, 6}

# 移除元素
my_set.remove(3)
print(my_set)  # 输出:{1, 2, 4, 5, 6}

# 集合操作
set1 = {1, 2, 3}
set2 = {3, 4, 5}
union_set = set1 | set2  # 并集
intersection_set = set1 & set2  # 交集
difference_set = set1 - set2  # 差集
print(union_set)  # 输出:{1, 2, 3, 4, 5}
print(intersection_set)  # 输出:{3}
print(difference_set)  # 输出:{1, 2}

字典

字典(Dictionary)是Python中的一种可变容器类型,用于存储键-值(key-value)对。字典中的键必须是唯一的且不可变的,而值可以是任意类型的对象。字典使用大括号{}来定义,并使用冒号:将键和值分隔开。

# 定义字典
my_dict = {"apple": 3, "banana": 5, "orange": 2}

# 访问字典元素
print(my_dict["apple"])

# 添加或修改元素
my_dict["apple"] = 4
my_dict["grape"] = 6
print(my_dict)

# 删除元素
del my_dict["banana"]
print(my_dict)

# 遍历字典
for key in my_dict:
    print(key, my_dict[key])

练习

练习1:在屏幕上显示跑马灯文字。

import os
import time

def marquee_text(text, width):
    while True:
        os.system('cls' if os.name == 'nt' else 'clear')  # 清空屏幕内容
        print(text.center(width))  # 将文字居中显示
        time.sleep(0.1)  # 暂停一段时间
        text = text[1:] + text[0]  # 将文字向左滚动

# 调用函数显示跑马灯文字
text_to_display = "This is a marquee text!"
screen_width = 80  # 屏幕宽度,根据实际情况设定
marquee_text(text_to_display, screen_width)

代码说明:首先定义了一个名为marquee_text的函数,该函数接受两个参数:text表示要显示的文字,width表示屏幕的宽度。函数使用无限循环来实现跑马灯效果。在每次循环中,我们使用os.system函数清空屏幕内容,并使用print函数将文字居中显示在屏幕上。然后,使用time.sleep函数暂停一段时间,以控制文字滚动的速度。接着,我们将文字向左滚动一位,实现跑马灯效果。

最后,我们在主程序中定义了要显示的跑马灯文字text_to_display和屏幕的宽度screen_width,然后调用marquee_text函数来显示跑马灯文字。

练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成。

import random
import string

def generate_verification_code(length):
    characters = string.ascii_letters + string.digits
    code = ''.join(random.choices(characters, k=length))
    return code

# 调用函数生成验证码
code_length = 6  # 验证码长度
verification_code = generate_verification_code(code_length)
print(verification_code)

说明: random.choices 函数来直接生成指定长度的验证码。函数接受两个参数:characters 是由大小写字母和数字构成的字符集合,k 是要生成的验证码的长度。通过将 random.choices 的结果使用 ‘’.join() 连接起来,我们获得了生成的验证码 code。

练习3:设计一个函数返回给定文件名的后缀名。

def get_file_extension(filename):
    if '.' in filename:
        return filename.rsplit('.', 1)[1]
    else:
        return ""

# 调用函数获取文件后缀名
file_name = "example.txt"
extension = get_file_extension(file_name)
print(extension)

说明:定义了一个名为 get_file_extension 的函数,它接受一个参数 filename,表示文件名。

函数内部,我们使用条件语句来检查文件名中是否包含点号(.)。如果包含,我们使用 rsplit 函数对文件名进行拆分,以最后一个点号为分隔符,拆分成两个部分。然后,我们返回拆分后的第二部分,即文件的后缀名。
如果文件名不包含点号,表示没有后缀名,我们返回一个空字符串。

练习4:设计一个函数返回传入的列表中最大和第二大的元素的值。

def find_max_and_second_max(lst):
    if len(lst) < 2:
        return None

    max_value = max(lst[0], lst[1])
    second_max_value = min(lst[0], lst[1])

    for i in range(2, len(lst)):
        if lst[i] > max_value:
            second_max_value = max_value
            max_value = lst[i]
        elif lst[i] > second_max_value:
            second_max_value = lst[i]

    return max_value, second_max_value

# 调用函数查找最大和第二大元素
my_list = [5, 9, 3, 1, 7, 2, 8]
max_value, second_max_value = find_max_and_second_max(my_list)
print("最大值:", max_value)
print("第二大值:", second_max_value)

说明:函数内部,我们先对输入列表的长度进行判断。如果列表长度小于2,即没有足够的元素来找到最大和第二大值,我们返回 None。

然后,我们初始化 max_value 和 second_max_value 分别为列表中的前两个元素。接下来,我们使用循环遍历列表,从第三个元素开始,逐个比较元素的大小。
如果当前元素大于 max_value,我们将 max_value 更新为当前元素,并将原来的 max_value 更新为 second_max_value。
如果当前元素介于 max_value 和 second_max_value 之间,我们将 second_max_value 更新为当前元素。

练习5:计算指定的年月日是这一年的第几天。

def calculate_day_of_year(year, month, day):
    days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

    # 判断闰年
    if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
        days_in_month[1] = 29

    total_days = sum(days_in_month[:month-1]) + day
    return total_days

# 调用函数计算指定日期是这一年的第几天
year = 2023
month = 5
day = 15
day_of_year = calculate_day_of_year(year, month, day)
print("指定日期是这一年的第", day_of_year, "天")

说明:函数内部,我们定义了一个列表 days_in_month,其中存储了每个月的天数(默认情况下,2月为28天)。然后,我们通过判断是否为闰年来更新2月份的天数为29天。对于闰年的判断条件是:年份能被4整除且不能被100整除,或者能被400整除。接下来,我们使用切片操作 days_in_month[:month-1] 来获取指定月份之前的天数总和,并将其与指定日期 day 相加,得到结果 total_days。最后,我们返回 total_days,表示指定日期是这一年的第几天。

练习6:打印杨辉三角。

def print_pascal_triangle(rows):
    triangle = []

    for i in range(rows):
        row = [1] * (i + 1)
        for j in range(1, i):
            row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j]
        triangle.append(row)

    for row in triangle:
        print(' '.join(str(num) for num in row))

# 调用函数打印杨辉三角
num_rows = 6
print_pascal_triangle(num_rows)

练习7:双色球

import random

def generate_lottery_numbers():
    red_numbers = random.sample(range(1, 34), 6)
    blue_number = random.randint(1, 16)
    return red_numbers, blue_number

# 调用函数生成双色球号码
red_numbers, blue_number = generate_lottery_numbers()
print("红色球号码:", red_numbers)
print("蓝色球号码:", blue_number)

说明:在 generate_lottery_numbers 函数中,我们使用 random.sample 函数从1到33的范围内选择不重复的6个红色球号码,并将它们存储在 red_numbers 列表中。然后,我们使用 random.randint 函数在1到16的范围内生成一个随机的蓝色球号码,并将它存储在 blue_number 变量中。最后,我们返回 red_numbers 列表和 blue_number 变量作为生成的双色球号码。

练习8:约瑟夫环问题。
“”"
《幸运的基督徒》
有15个基督徒和15个非基督徒在海上遇险,为了能让一部分人活下来不得不将其中15个人扔到海里面去,有个人想了个办法就是大家围成一个圈,由某个人开始从1报数,报到9的人就扔到海里面,他后面的人接着从1开始报数,报到9的人继续扔到海里面,直到扔掉15个人。由于上帝的保佑,15个基督徒都幸免于难,问这些人最开始是怎么站的,哪些位置是基督徒哪些位置是非基督徒。
“”"

def find_initial_positions():
    people = [1] * 15 + [0] * 15
    positions = list(range(1, 31))
    index = 0

    while len(people) > 15:
        index = (index + 8) % len(people)
        people.pop(index)
        positions.pop(index)

    return positions

# 调用函数查找最开始的位置
initial_positions = find_initial_positions()

# 打印结果
for i, position in enumerate(initial_positions):
    if i == 14:
        print("基督徒", position)
    else:
        print("非基督徒", position)

说明:我们创建了一个长度为30的列表 people,其中前15个元素为1(基督徒),后15个元素为0(非基督徒)。然后,我们创建一个列表 positions,其中包含了从1到30的所有位置。接下来,我们使用一个循环来模拟报数和扔人的过程,直到只剩下15个人。循环的条件是列表 people 的长度大于15,表示还有多于15个人在环中。在循环中,我们使用 (index + 8) % len(people) 的方式来确定当前报数到9的人的索引。然后,我们使用 pop 函数将该人从列表 people 和 positions 中移除。最后,当循环结束时,列表 people 中只剩下15个人,我们将剩下的位置作为结果返回。

练习9:井字棋游戏。
井字棋(Tic-Tac-Toe)是一种经典的两人对弈游戏,通常在3x3的格子上进行。两名玩家轮流在空格中放置自己的标记(一方为"X",另一方为"O"),目标是将自己的标记在横、竖或斜线上连成一条直线,先达成这个目标的玩家获胜。

def print_board(board):
    for row in board:
        print("|".join(row))
        print("-" * 9)

def check_winner(board):
    # 检查行
    for row in board:
        if row[0] == row[1] == row[2] != " ":
            return row[0]

    # 检查列
    for col in range(3):
        if board[0][col] == board[1][col] == board[2][col] != " ":
            return board[0][col]

    # 检查对角线
    if board[0][0] == board[1][1] == board[2][2] != " ":
        return board[0][0]
    if board[0][2] == board[1][1] == board[2][0] != " ":
        return board[0][2]

    return None

def play_game():
    board = [[" ", " ", " "],
             [" ", " ", " "],
             [" ", " ", " "]]

    current_player = "X"
    is_game_over = False

    while not is_game_over:
        print_board(board)

        row = int(input("请输入行号 (0-2): "))
        col = int(input("请输入列号 (0-2): "))

        if board[row][col] == " ":
            board[row][col] = current_player

            winner = check_winner(board)
            if winner:
                print_board(board)
                print("玩家", winner, "获胜!")
                is_game_over = True
            elif " " not in board[0] and " " not in board[1] and " " not in board[2]:
                print_board(board)
                print("平局!")
                is_game_over = True
            else:
                current_player = "O" if current_player == "X" else "X"
        else:
            print("该位置已被占据,请重新选择。")

# 开始游戏
play_game()

说明:print_board 函数用于打印当前游戏棋盘的状态。

check_winner 函数用于检查当前棋盘是否有玩家获胜。它首先检查行、列和对角线是否有连成一条线的相同标记,如果有,就返回获胜的玩家标记,否则返回 None。

play_game 函数用于驱动整个游戏过程。它初始化一个空的3x3棋盘,然后循环进行玩家输入和判断获胜的步骤。每次轮到玩家时,打印当前棋盘状态,并要求玩家输入行号和列号来放置自己的标记。如果玩家放置成功后获胜,或者棋盘已满而无法获胜,则游戏结束。

优化代码(了解即可,不用深入学习)
思路:减少重复计算:在检查获胜条件时,可以避免重复计算相同的行、列和对角线。可以在每次玩家放置标记后,仅检查可能受到影响的行、列和对角线。
输入验证:对玩家的输入进行验证,确保输入的行号和列号在有效范围内,并且选择的位置没有被占据。
引入AI玩家:添加一个简单的AI玩家作为对手,使游戏可以在单人模式下进行。可以使用简单的算法来选择AI玩家的下一步动作,例如随机选择一个空格或使用基本的启发式算法。
代码如下:

import random

def print_board(board):
    for row in board:
        print("|".join(row))
        print("-" * 9)

def check_winner(board, player):
    for i in range(3):
        if board[i][0] == board[i][1] == board[i][2] == player:
            return True
        if board[0][i] == board[1][i] == board[2][i] == player:
            return True
    if board[0][0] == board[1][1] == board[2][2] == player:
        return True
    if board[0][2] == board[1][1] == board[2][0] == player:
        return True
    return False

def play_game():
    board = [[" " for _ in range(3)] for _ in range(3)]
    players = ["X", "O"]
    current_player = random.choice(players)
    is_game_over = False

    while not is_game_over:
        print_board(board)
        if current_player == "X":
            row = int(input("请输入行号 (0-2): "))
            col = int(input("请输入列号 (0-2): "))
            if row < 0 or row > 2 or col < 0 or col > 2 or board[row][col] != " ":
                print("无效的位置,请重新选择。")
                continue
        else:
            print("AI 玩家正在思考中...")
            row, col = make_ai_move(board)

        board[row][col] = current_player

        if check_winner(board, current_player):
            print_board(board)
            if current_player == "X":
                print("玩家 X 获胜!")
            else:
                print("AI 玩家获胜!")
            is_game_over = True
        elif all(board[i][j] != " " for i in range(3) for j in range(3)):
            print_board(board)
            print("平局!")
            is_game_over = True
        else:
            current_player = players[1 - players.index(current_player)]

def make_ai_move(board):
    # 随机选择一个空格放置标记
    empty_spaces = [(i, j) for i in range(3) for j in range(3) if board[i][j] == " "]
    return random.choice(empty_spaces)

# 开始游戏
play_game()

说明:在 check_winner 函数中,我们减少了重复计算,仅检查可能受到影响的行、列和对角线。

对玩家的输入进行了验证,确保输入的行号和列号在有效范围内,并且选择的位置没有被占据。

引入了一个简单的AI玩家作为对手。在 make_ai_move 函数中,我们随机选择一个空格放置AI玩家的标记。

在游戏主循环中,根据当前玩家是人类玩家还是AI玩家,进行相应的操作。

Day 7 is over!

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值