井字棋游戲編程,python編寫井字棋_編寫井字游戲

 2023-10-13 阅读 30 评论 0

摘要:python編寫井字棋 Programming computer games may be the most technically challenging (and possibly the best paying) job that a programmer can have. Top level games require the best from both programmers and computers. 對計算機游戲進行編程可能是程序員可以從

python編寫井字棋

Programming computer games may be the most technically challenging (and possibly the best paying) job that a programmer can have. Top level games require the best from both programmers and computers.

對計算機游戲進行編程可能是程序員可以從事的技術上最具挑戰性的工作(可能是報酬最高的工作)。 頂級游戲需要程序員和計算機的鼎力支持。

Visual Basic 6 has now been thoroughly bypassed as a platform for game programming. (It never really was one. Even in the "good ol' days", serious game programmers would never use a high-level language like VB 6 because you just couldn't get the cutting edge performance that most games require.) But the simple "Tic Tac Toe" game is a great introduction to programming that is a little more advanced than "Hello World!"

現在, Visual Basic 6已被完全繞開作為游戲編程平臺。 (它從來不是真正的一個。即使在“好日子”中,嚴肅的游戲程序員也永遠不會使用VB 6這樣的高級語言,因為您無法獲得大多數游戲所要求的最先進的性能。)但是簡單的“井字游戲”是對編程的一個很好的介紹,它比“ Hello World!”要先進一些。

This is a great introduction to many of the fundamental concepts of programming since it combines techniques including:

這是對許多編程基本概念的精彩介紹,因為它結合了以下技術:

  • The use of arrays. The X and O markers are kept in separate arrays and the entire arrays are passed between functions to keep track of the progress of the game.

    井字棋游戲編程。 使用數組 。 X和O標記保存在單獨的數組中,整個數組在函數之間傳遞,以跟蹤游戲的進度。

  • Using VB 6 level graphics: VB 6 doesn't offer great graphical capability, but the game is a good introduction to what is available. Much of the rest of this series is an exploration of how GDI+, the next generation of Microsoft graphics, replaces the VB 6 level graphics.

    使用VB 6級別的圖形:VB 6沒有提供強大的圖形功能,但是該游戲很好地介紹了可用的功能。 本系列其余大部分內容探討了下一代Microsoft圖形GDI +如何替代VB 6級圖形。
  • Using math calculations for program control: The program uses clever modulo (Mod) and integer division calculations using the two-game marker arrays to determine when a three-element "win" has occurred.

    使用數學計算進行程序控制:程序使用巧妙的模數(Mod)和整數除法計算,并使用兩個游戲標記數組確定何時發生了三元素“勝利”。

The class of programming in this article is perhaps just a little past the beginning level but it should be good for "intermediate" programmers. But let's start at an elementary level to illustrate some of the concepts and get you started with your Visual Basic game programming career. Even students more advanced than that may find that it's slightly challenging to get the objects in the form just right.

本文中的編程類別可能僅比入門水平稍高一點,但是對“中級”程序員來說應該是不錯的選擇。 但是,讓我們從基本的層次開始說明一些概念,并開始您的Visual Basic游戲編程生涯。 即使是比這更高級的學生,也可能會發現,正確放置表單中的對象有些挑戰。

如何玩井字游戲 ( How to Play Tic Tac Toe )

If you've never played Tic Tac Toe, here are the rules. Two players alternate at placing Xs and Os into 3 x 3 playing field.

如果您從未玩過Tic Tac Toe ,請遵循以下規則。 兩名玩家交替將X和O放置在3 x 3的游戲場地中。

Before the game starts, both players have to agree about who will go first and who will mark his moves with which symbol. After the first move, the players alternately place their marks in any empty cell. The goal of the game is to be the first player with three marks in a horizontal, diagonal or vertical line. If there are no empty cells and neither player has a winning combination, the game is a draw.

在游戲開始之前,兩個玩家都必須就誰先走以及誰將用哪個符號標記自己的動作達成一致。 第一步之后,玩家將標記交替放置在任何空白單元格中。 游戲的目標是成為第一個在水平,對角線或垂直線上帶有三個標記的玩家。 如果沒有空單元,并且沒有玩家有獲勝組合,則該游戲為平局。

啟動程序 ( Starting the Program )

Before starting any actual coding, it's always a good idea to change the names of any components you use. Once you start coding, the name will be used automatically by Visual Basic so you want it to be the right name. We'll use the form name frmTicTacToe and we'll also change the caption to "About Tic Tac Toe."

在開始任何實際編碼之前,最好更改您使用的任何組件的名稱。 一旦開始編碼 ,Visual Basic將自動使用該名稱,因此您希望它是正確的名稱。 我們將使用表單名稱frmTicTacToe ,并將標題更改為“關于井字游戲”。

With the form established, use the line toolbox control to draw a 3 x 3 grid. Click the line tool, then draw a line where you want it. You'll have to create four lines this way and adjust their length and position to make them look right. Visual Basic also has some convenient tools under the Format menu that will help. This is a great chance to practice with them.

建立表單后,使用線條工具箱控件繪制一個3 x 3的網格。 單擊線條工具,然后在所需位置畫一條線。 您必須以這種方式創建四行,并調整其長度和位置以使其看起來正確。 Visual Basic在“格式”菜單下還提供了一些方便的工具,這些工具會有所幫助。 這是和他們一起練習的絕好機會。

In addition to the playing grid, we'll need some objects for the X and O symbols that will be placed on the grid. Since there are nine spaces in the grid, we'll create an object array with nine spaces, called elements in Visual Basic.

除了播放網格外,我們還需要一些將用于放置在網格上的X和O符號的對象。 由于網格中有9個空格,因此我們將創建一個具有9個空格的對象數組,在Visual Basic中稱為元素。

There are several ways to do just about everything in the Visual Basic development environment, and creating control arrays is is no exception. Probably the easiest way is to create the first label (click and draw just like the line tool), name it, set all of the attributes (such as Font and ForeColor), and then make copies of it. VB 6 will ask if you want to create a control array. Use the name lblPlayGround for the first label.

在Visual Basic開發環境中,有幾種方法可以完成所有事情,創建控件數組也不例外。 可能最簡單的方法是創建第一個標簽(單擊并繪制,就像線條工具一樣),命名它,設置所有屬性(例如Font和ForeColor),然后復制它。 VB 6將詢問您是否要創建控件數組。 將名稱lblPlayGround用作第一個標簽。

To create the other eight elements of the grid, select the first label object, set the Index property to zero, and press CTRL+C (copy). Now you can press CTRL+V (paste) to create another label object. When you copy objects like this, each copy will inherit all properties except Index from the first one. Index will increase by one for each copy. This is a control array because they all have the same name, but different index values.

若要創建網格的其他八個元素,請選擇第一個標簽對象,將Index屬性設置為零,然后按CTRL + C(復制)。 現在,您可以按CTRL + V(粘貼)來創建另一個標簽對象。 當您像這樣復制對象時,每個副本都將繼承除第一個索引以外的所有屬性。 每個副本的索引將增加一。 這是一個控制數組,因為它們都具有相同的名稱,但索引值不同。

If you create the array this way, all of the copies will be stacked on top of each other in the upper left corner of the form. Drag each label to one of the playing grid positions. Be sure that index values are sequential in the grid. The logic of the program depends on it. The label object with index value 0 should be in the top left corner, and the bottom right label should have index 8. If the labels cover the playing grid, select each label, right-click, and select Send to Back.

如果以這種方式創建陣列,則所有副本將在窗體的左上角彼此堆疊。 將每個標簽拖到播放網格位置之一。 確保索引值在網格中是連續的。 程序的邏輯取決于它。 索引值為0的標簽對象應位于左上角,而右下方的標簽應具有索引8。如果標簽覆蓋了播放網格,請選擇每個標簽,單擊鼠標右鍵,然后選擇“發送回”。

Since there are eight possible ways to win the game, we'll need eight different lines to show the win on the playing grid. You will use the same technique to create another control array. First, draw the line, name it linWin, and set the Index property to zero. Then use copy-paste technique to produce seven more lines. The following illustration shows how to set the index numbers correctly.

由于有八種可能的方式來贏得比賽,因此我們需要八條不同的線來顯示比賽中的勝利。 您將使用相同的技術來創建另一個控件數組。 首先,繪制線條,將其命名為linWin,并將Index屬性設置為零。 然后使用復制粘貼技術再產生七行。 下圖顯示了如何正確設置索引號。

In addition to the label and line objects, you need some command buttons to play the game and more labels to keep score. The steps to create these are not detailed here, but these are the objects you need.

除了標簽和線條對象之外,您還需要一些命令按鈕來玩游戲,還需要更多標簽來保持得分。 創建這些步驟的步驟不在此處詳述,但是這些是您需要的對象。

Two button objects:

兩個按鈕對象 :

  • cmdNewGame

    cmdNewGame
  • cmdResetScore

    cmdResetScore

Frame object fraPlayFirst containing two option buttons:

框架對象fraPlayFirst包含兩個選項按鈕:

  • optXPlayer

    optXPlayer
  • optOPlayer

    optOPlayer

Frame object fraScoreBoard containing six labels. Only lblXScore and lblOScore are changed in the program code.

框架對象fraScoreBoard包含六個標簽。 程序代碼中僅lblXScore和lblOScore被更改。

  • lblX

    bl
  • lblXScore

    lblXScore
  • lblO

    bl
  • lblOScore

    lblOScore
  • lblMinus

    lblMinus
  • lblColon

    lbl冒號

Finally, you also need the label object lblStartMsg to 'mask' the cmdNewGame button when it shouldn't be clicked. This isn't visible in the illustration below because it occupies the same space in the form as the command button. You may have to move the command button temporarily to draw this label on the form.

最后,當不應該單擊cmdNewGame按鈕時,還需要標簽對象lblStartMsg對其進行“遮罩”。 這在下圖中不可見,因為它在窗體中與命令按鈕占據相同的空間。 您可能必須暫時移動命令按鈕才能在表單上繪制此標簽。

So far, no VB coding has been done, but we're finally ready to do that.

到目前為止,還沒有完成VB編碼,但是我們終于準備好了。

初始化 ( Initialization )

Now you get to finally start coding the program. If you haven't already, you might want to download the source code to follow along as the operation of the program is explained.

現在,您終于可以開始對程序進行編碼了。 如果尚未下載源代碼,則可能需要下載源代碼以跟隨程序的操作說明。

One of the first design decisions to make is how to keep track of the current 'state' of the game. In other words, what are the current Xs and Os on the playing grid and who moves next. The concept of 'state' is critical in a lot of programming, and in particular, it's important in programming ASP and ASP.NET for the web

首先要做出的設計決定之一就是如何跟蹤游戲的當前“狀態”。 換句話說,當前在游戲網格上的X和O是什么,接下來是誰。 “狀態”的概念在許多編程中都至關重要,尤其是在為Web編程ASP和ASP.NET時很重要

There are several ways that this could be done, so it's a critical step in the analysis. If you were solving this problem on your own, you might want to draw a flowchart and try out different options with 'scratch paper' before starting any coding.

有幾種方法可以完成此操作,因此這是分析中的關鍵步驟。 如果您自己解決此問題,則可能需要繪制流程圖并嘗試使用“草稿紙”嘗試其他選項,然后再開始編碼。

變數 ( Variables )

Our solution uses two "two-dimensional arrays" because that helps keep track of 'state' by simply changing the array indexes in program loops. The state of the top-left corner will be in the array element with index (1, 1), the top-right corner will be in (1, 3), the bottom-right in (3,3), and so forth. The two arrays that do this are:

我們的解決方案使用兩個“二維數組”,因為這可以通過簡單地在程序循環中更改數組索引來幫助跟蹤“狀態”。 左上角的狀態將在索引為(1,1)的數組元素中,右上角將在(1,3)中,右下角在(3,3)中,依此類推。 執行此操作的兩個數組是:

iXPos(x, y)
iXPos(x,y)

and

iOPos(x, y)
iOPos(x,y)

There are a lot of different ways this can be done and the final VB.NET solution in this series shows you how to do it with just a single one-dimensional array.

有很多不同的方法可以完成此操作,本系列的最終VB.NET解決方案將向您展示如何僅使用一個一維數組來完成此操作。

The programming to translate these arrays into player win decisions and visible displays in the form are on the next page.

在下一頁上將這些數組轉換為玩家獲勝決定和表格中可見顯示的程序。

You also need a few global variables as follows. Notice that these are in the General and Declarations code for the form. This makes them "module level" variables that can be referenced anywhere in the code for this form. For more on this, check Understanding the Scope of Variables in Visual Basic Help.

您還需要一些全局變量,如下所示。 請注意,這些在表單的“常規”和“聲明”代碼中。 這使它們成為“模塊級”變量,可以在此表單的代碼中的任何地方引用。 有關此的更多信息,請在Visual Basic幫助中選中“了解變量的范圍”。

There are two areas where variables are initialized in our program. First, a few variables are initialized while the form frmTicTacToe is loading.

在我們的程序中有兩個初始化變量的區域。 首先,在加載frmTicTacToe表單時初始化一些變量。

Private Sub Form_Load()
私人子Form_Load()

Second, before each new game, all variables that need to be reset to starting values are assigned in an initialization subroutine.

其次,在每個新游戲之前,所有需要重置為初始值的變量都在初始化子例程中分配。

Sub InitPlayGround()
子InitPlayGround()

Note that the form load initialization also calls the playground initialization.

請注意,表單加載初始化也稱為游樂場初始化。

One of the critical skills of a programmer is the ability to use the debugging facilities to understand what the code is doing. You can use this program to try:

程序員的一項關鍵技能是能夠使用調試工具來了解代碼在做什么。 您可以使用此程序嘗試:

  • Stepping through the code with the F8 key

    使用F8鍵單步執行代碼
  • Setting a watch on key variables, such as sPlaySign or iMove

    監視關鍵變量,例如sPlaySign或iMove

    c語言井字棋代碼、Setting a breakpoint and querying the value of variables. For example, in the inner loop of the initialization:

    設置斷點并查詢變量的值。 例如,在初始化的內部循環中:

lblPlayGround((i - 1) * 3 + j - 1).Caption = ""
lblPlayGround((i-1)* 3 + j-1).Caption =“”

Note that this program clearly shows why it's a good programming practice to keep data in arrays whenever possible. If you did not have arrays in this program, you would have to write code something like this:

請注意,該程序清楚地說明了為什么盡可能將數據保留在數組中是一種好的編程習慣。 如果此程序中沒有數組,則必須編寫如下代碼:


Line1.Visible = False
Line1.Visible = False
Line2.Visible = False
Line2.Visible = False
Line3.Visible = False
Line3.Visible = False
Line4.Visible = False
Line4.Visible = False
Line5.Visible = False
Line5.Visible = False
Line6.Visible = False
Line6.Visible = False
Line7.Visible = False
Line7.Visible = False

instead of this:

代替這個:


linWin(i).Visible = False
linWin(i).Visible = False
Next i
接下來我

采取行動 ( Making a Move )

If any part of the system can be thought of as 'the heart', it's subroutine lblPlayGround_Click. This subroutine is called every time a player clicks the playing grid. (Clicks must be inside one of the nine lblPlayGround elements.) Notice that this subroutine has an argument: (Index As Integer). Most of the other 'event subroutines', like cmdNewGame_Click() do not. Index indicates which label object has been clicked. For example, index would contain the value zero for the top-left corner of the grid and the value eight for the bottom-right corner.

如果系統的任何部分都可以視為“心臟”,則它是子例程lblPlayGround_Click。 每當玩家單擊播放網格時,都會調用此子例程。 (單擊必須在9個lblPlayGround元素之一之內。)請注意,此子例程具有一個參數:(整數索引)。 其他大多數“事件子例程”(例如cmdNewGame_Click())都沒有。 索引指示已單擊的標簽對象。 例如,索引將包含網格左上角的值零和右下角的值八。

After a player clicks a square in the game grid, the command button to start another game, cmdNewGame, is "turned on' by making it visible. The state of this command button does double duty because it's also used as a boolean decision variable later in the program. Using a property value as a decision variable is usually discouraged because if it ever becomes necessary to change the program (say, for example, to make the cmdNewGame command button visible all the time), then the program will unexpectedly fail because you might not remember that it's also used as part of the program logic. For this reason, it's always a good idea to search through program code and check the use of anything you change when doing program maintenance, even property values. This program violates the rule partly to make this point and partly because this is a relatively simple piece of code where it's easier to see what is being done and avoid problems later.

玩家單擊游戲網格中的正方形后,通過使其可見而“打開”了用于啟動另一個游戲cmdNewGame的命令按鈕。此命令按鈕的狀態起著雙重作用,因為稍后它也用作布爾決策變量通常不建議使用屬性值作為決策變量,因為如果有必要更改程序(例如,使cmdNewGame命令按鈕始終可見),則程序將意外失敗,因為您可能不記得它也被用作程序邏輯的一部分,因此,搜索程序代碼并檢查在進行程序維護時更改的內容(甚至是屬性值)的使用始終是一個好主意。規則部分是為了說明這一點,部分是因為這是一段相對簡單的代碼,在其中更容易看到正在執行的操作并避免以后出現問題。

A player selection of a game square is processed by calling the GamePlay subroutine with Index as the argument.

通過調用以Index為參數的GamePlay子例程來處理游戲方的玩家選擇。

處理移動 ( Processing the Move )

First, you check to see if an unoccupied square was clicked.

首先,您檢查是否單擊了未占用的正方形。

If lblPlayGround(xo_Move).Caption = "" Then
如果lblPlayGround(xo_Move).Caption =“”然后

Once we're sure this is a legitimate move, the move counter (iMove) is incremented. The next two lines are very interesting since they translate the coordinates from the one-dimensional If lblPlayGround component array to two-dimensional indexes that you can use in either iXPos or iOPos. Mod and integer division (the 'backslash') are mathematical operations that you don't use every day, but here's a great example showing how they can be very useful.

一旦確定這是合法的移動,移動計數器(iMove)就會增加。 接下來的兩行非常有趣,因為它們將坐標從一維If lblPlayGround組件數組轉換為可以在iXPos或iOPos中使用的二維索引。 Mod和整數除法(“反斜杠”)是您每天都不會使用的數學運算,但是,這是一個很好的示例,展示了它們如何非常有用。


iMove = iMove + 1
iMove = iMove +1
x = Int(xo_Move / 3) + 1
x =整數(xo_Move / 3)+ 1
y = (xo_Move Mod 3) + 1
y =(xo_Move Mod 3)+ 1

The xo_Move value 0 will be translated to (1, 1), 1 to (1, 2) ... 3 to (2, 1) ... 8 to (3, 3).

xo_Move值0將轉換為(1,1),1轉換為(1,2)... 3轉換為(2,1)... 8轉換為(3,3)。

The value in sPlaySign, a variable with module scope, keeps track of which player made the move. Once the move arrays are updated, the label components in the playing grid can be updated with the appropriate sign.

sPlaySign中的值(具有模塊范圍的變量)會跟蹤哪個玩家進行了移動。 一旦更新了移動數組,就可以使用適當的符號來更新播放網格中的標簽組件。


iOPos(x, y) = 1
iOPos(x,y)= 1
iWin = CheckWin(iOPos())
iWin = CheckWin(iOPos())
Else
其他
iXPos(x, y) = 1
iXPos(x,y)= 1
iWin = CheckWin(iXPos())
iWin = CheckWin(iXPos())
End If
萬一
lblPlayGround(xo_Move).Caption = sPlaySign
lblPlayGround(xo_Move).Caption = sPlaySign

For example, when the X player clicks the top left corner of the grid, variables will have the following values:

例如,當X播放器單擊網格的左上角時,變量將具有以下值:

The user screen shows only an X in the upper left box, while the iXPos has a 1 in the upper left box and 0 in all of the others. The iOPos has 0 in every box.

用戶屏幕在左上方的框中僅顯示一個X,而iXPos在左上方的框中顯示為1,其他所有字段均為0。 iOPos在每個框中都有0。

The values changes when the O player clicks the center square of the grid. Now th iOPos shows a 1 in the center box while the user screen shows an X in the upper left and an O in the center box. The iXPos shows only the 1 in the upper left corner, with 0 in all of the other boxes.

當O播放器單擊網格的中心正方形時,值會更改。 現在,iOPos在中心框中顯示1,而用戶屏幕在左上方顯示X,在中心框中顯示O。 iXPos在左上角僅顯示1,在所有其他框中顯示0。

Now that you know where a player clicked, and which player did the clicking (using the value in sPlaySign), all you have to do is find out if someone won a game and figure out how to show that in the display.

既然您知道了某個玩家單擊的位置以及該玩家單擊的位置(使用sPlaySign中的值),那么您所要做的就是找出某人是否贏得了一場比賽,并弄清楚如何在屏幕上顯示出來。

尋找贏家 ( Finding a Winner )

After each move, the CheckWin function checks for the winning combination. CheckWin works by adding down each row, across each column and through each diagonal. Tracing the steps through CheckWin using Visual Basic's Debug feature can be very educational. Finding a win is a matter of first, checking whether three 1's were found in each of the individual checks in the variable iScore, and then returning a unique "signature" value in Checkwin that is used as the array index to change the Visible property of one element in the linWin component array. If there is no winner, CheckWin will contain the value -1. If there is a winner, the display is updated, the scoreboard is changed, a congratulation message is displayed, and the game is restarted.

每次移動后,CheckWin函數都會檢查獲勝組合。 CheckWin的工作原理是將每一行,每一列和每個對角線相加。 使用Visual Basic的“調試”功能通過CheckWin跟蹤步驟可能非常有教育意義。 首先要找到一個勝利,首先檢查在變量iScore的每個檢查中是否都找到三個1,然后在Checkwin中返回一個唯一的“簽名”值,該值用作更改數組的Visible屬性的數組索引。 linWin組件數組中的一個元素。 如果沒有獲勝者,CheckWin將包含值-1。 如果有贏家,則更新顯示,更改記分牌,顯示祝賀消息,然后重新開始比賽。

Let's go through one of the checks in detail to see how it works. The others are similar.

讓我們詳細檢查其中一項以了解其工作原理。 其他類似。


For i = 1 To 3
對于i = 1至3
iScore = 0
iScore = 0
CheckWin = CheckWin + 1
CheckWin = CheckWin +1
For j = 1 To 3
對于j = 1到3
iScore = iScore + iPos(i, j)
iScore = iScore + iPos(i,j)
Next j
下一個j
If iScore = 3 Then
如果iScore = 3,則
Exit Function
退出功能
End If
萬一
Next i
接下來我

The first thing to notice is that the first index counter i counts down the rows while the second j counts across the columns. The outer loop, then simply moves from one row to the next. The inner loop counts the 1's in the current row. If there are three, then you have a winner.

首先要注意的是,第一個索引計數器i對行進行遞減計數,而第二個索引計數器i對列進行計數。 然后,外循環僅從一行移到下一行。 內部循環在當前行中計數1。 如果有三個,那么您就有贏家。

Notice that you also keep track of the total number of squares tested in the variable CheckWin, which is the value passed back when this function terminates. Each winning combination will end up with a unique value in CheckWin from 0 to 7 which is used to select one of the elements in the linWin() component array. This makes the order of the code in function CheckWin important too! If you moved one of the blocks of loop code (like the one above), the wrong line would be drawn on the playing grid when someone wins. Try it and see!

請注意,您還可以在CheckWin變量中跟蹤測試的平方總數,該變量是此函數終止時傳回的值。 每個獲勝組合最終將在CheckWin中從0到7擁有一個唯一值,該唯一值用于選擇linWin()組件數組中的元素之一。 這也使功能CheckWin中的代碼順序很重要! 如果您移動了循環代碼塊之一(如上面的代碼塊),則當有人獲勝時,會在播放網格上畫錯線。 試試看!

整理細節 ( Finishing Details )

The only code not yet discussed is the subroutine for a new game and the subroutine that will reset the score. The rest of the logic in the system makes creating these quite easy. To start a new game, you have only to call the InitPlayGround subroutine. As a convenience for players since the button could be clicked in the middle of a game, you ask for confirmation before going ahead. You also ask for confirmation before restarting the scoreboard.

尚未討論的唯一代碼是新游戲的子例程和將重置得分的子例程。 系統中的其余邏輯使創建這些邏輯變得非常容易。 要開始新游戲,您只需調用InitPlayGround子例程。 為了方便玩家,因為可以在游戲過程中單擊按鈕,因此在進行下一步操作之前,您需要先進行確認。 您還需要在重新啟動記分板之前進行確認。

翻譯自: https://www.thoughtco.com/programming-the-tic-tac-toe-game-4079040

c語言井字棋。python編寫井字棋

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/142306.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息