联系方式

  • QQ:99515681
  • 邮箱:99515681@qq.com
  • 工作时间:8:00-21:00
  • 微信:codinghelp

您当前位置:首页 >> C/C++编程C/C++编程

日期:2022-05-26 07:45

Revision 2

Software Development with C++ - Assignment 1 Page 1 of 13

Software Development with C++

Assignment 1

Dungeon Crawler

Introduction

The assignment will require you to design, implement, test, and debug a text-based Roguelike dungeon crawler

game using object-oriented methods and the application of appropriate design patterns. In addition, it will

require to document your classes appropriately using Doxygen comments. The work in this assignment is to be

performed in groups of 4–5 and will be submitted via Cloud Campus in week 17. Please refer to the Cloud

Campus website for exact due dates and times.

Learning Outcomes

After completing this assignment, you will have learnt to:

? Design a class hierarchy using UML

? Apply Object-Oriented principles (encapsulation, reuse, etc.) and Design Patterns to the software design

? Implement the software design in C++ using class inheritance and polymorphism features

? Write well-behaved constructors and destructors for C++ classes

? Use C++ pointers (including smart pointers) and dynamic memory allocation and management

? Use stream I/O and manipulators for formatted input and output

? Write code adhering to coding standards, guidelines and good programming practices

Task Description

Roguelike Dungeon Crawler games are a genre of game that exhibit features typically attributed to the 1980s

computer game Rogue1

. In general, the goal of a Roguelike game is to clear a dungeon, level-by-level, until the

final level is completed. Common features of a Roguelike game include:

1. procedurally generated dungeons,

2. character creation,

3. randomised items and weapons,

4. permanent death, and

5. a variety of creatures to fight that get progressively more difficult deeper into the dungeon.

In this assignment, you will complete Features 1 and 2 from the above list. You will design and implement a

simple text-based Roguelike Dungeon Crawler game that fulfils the following specification. The assignment will be

driven by a text-based menu interface.

The aim of the assignment is to allow you to practise creating an object-oriented design and implementing it in

C++ using the features learnt so far: class inheritance and polymorphism, dynamic memory allocation, and smart

pointers. The focus of the assignment is on identifying and applying appropriate Design Patterns to help develop

the game in such a way that components are easily reusable and extensible.

1 https://en.wikipedia.org/wiki/Rogue_(video_game)

Revision 2

Software Development with C++ - Assignment 1 Page 2 of 13

First, you need to develop a class design using UML class diagrams based on the requirements in the remainder

of this specification. The UML class diagram must include all classes, public data members, public member

functions, protected data members and member functions (if any), and associations between classes.

For this assignment the game has been simplified. Some of the underlying elements (such as the basic menu

interface code) will be provided to get you started. The provided code will be incomplete and may need to be

modified to fit your class design. Parts of the provided code that must not be modified will be marked as such.

You will then need to implement the rest of the game according to the specifications and your design.

A brief example of how the game will be played is provided below. The remainder of the section describes the

requirements of the game in more detail.

> dungeon_crawler.exe

Welcome to Ashes of Software Development: Rite of Passage!

Developed by Matt Selway

COMP 3023 Software Development with C++

*Press any key to continue*

> [Enter]

What would you like to do?

(p)lay the game

(q)uit

> p

You need to create a character... what is your name?

> Dungeon Crusher

Welcome Dungeon Crusher, you have *6* stat points to allocate.

A high Strength stat will boost your damage in combat.

How many points do you want to add to your Strength?

> 3

You have *3* stat points remaining.

A high Dexterity stat will increase your ability to dodge creature attacks.

How many points do you want to add to you Dexterity?

> 2

You have *1* stat point remaining.

A high Wisdom stat will boost the effectiveness of magical items.

How many points do you want to add to your Wisdom?

> 1

*** Character Summary ***

Dungeon Crusher

Strength: 4

Dexterity: 3

Wisdom: 2

Health: 50 / 50

Damage: 10 – 10

Dodge: 20%

Weapon: you look down at your fists and shrug, "these will do"

Item: you look into your backpack, emptiness looks back.

if only you had something to put in it.

While roaming the country side you encounter a strange fork in the road.

To the left lies a dark cave, the foul stench of rotting flesh emanates from it.

To the right is a mysterious tower, a strange magical energy lights the path.

What would you like to do?

Go left: create a (b)asic dungeon

Go right: create a (m)agical dungeon

Go (b)ack the way you came (return to main menu)

> l

You enter the dark cave and see...

Revision 2

Software Development with C++ - Assignment 1 Page 3 of 13

A dark and empty chamber.

To the NORTH you see an opening to another chamber.

To the SOUTH you see the entrance by which you came in.

What would you like to do?

Go (n)orth

Go (b)ack the way you came

View your (c)haracter stats

Return to the main (m)enu

> m

After exploring *0* levels, you run out of the cave as quick as your legs can carry you.

As you emerge from the cave you are startled by the bright light and pause while your eyes

adjust.

What would like to do?

(p)lay the game

(q)uit

> q

*Are you sure you want to quit? (y/n) *

> y

Goodbye!

Note: the following conventions will be used when illustrating example output:

? a ‘>’ at the beginning of a line indicates the command line prompt

? bold orange text indicates input entered by the user

? user input surrounded in square brackets ‘[…]’ indicates a specific key press

? hitting [Enter] after other input is implied

Workplan

In order to complete this assignment, it is important that you take a planned approach to the assignment. You will

need to think about how to break down the assignment into smaller chunks of functionality that can be

implemented and tested one at a time. Working in an incremental manner, and testing as you progress, helps to

limit errors.

This assignment should be completed in the following stages:

1. Read the assignment specification (more than once)

2. Ensure you understand the 2 design patterns

3. Design a class hierarchy using UML class diagrams that addresses the requirements and applies the 2

design patterns to the game appropriately. Note: the design does not have to be perfectly complete from

the outset, you can revise it as you progress through the assignment. However, it is good to start by

conceptualising your approach before diving into the implementation.

4. Implement character generation—refer to section Player Character

5. Implement the basic dungeon: walls and doors, no creatures nor items (standardised layout)—refer to

section Dungeon (Rooms, Walls, Doors)

6. Implement dungeon navigation: at this point you should be able to play the game and transition between

rooms and dungeon levels without combat—refer to sections Dungeon (Rooms, Walls, Doors) and

General Gameplay

As you implement the functionality, you should tick, highlight, or otherwise mark the requirements that you have

completed. This will help you keep track of your progress and ensure you have addressed all the requirements in

your submission.

Revision 2

Software Development with C++ - Assignment 1 Page 4 of 13

General Gameplay

The gameplay state will be controlled by a Game object. There can only ever be one game in play at one time.

The game allows a player character to explore a dungeon of your choice, room-by-room.

When the game is first started, a welcome message will be displayed and the user will be given the option to play

the game or quit from the main menu.

If the user chooses to quit, the game will prompt the user to confirm that they want to quit the game:

? If the user selects ‘yes’, the game will end.

? If the user selects ‘no’, the game will return to the main menu.

If the user chooses to play the game (from the main menu), the game will prompt the user to create a character.

Refer to section Player Character for details on character creation.

After the user has created their character, the game will prompt the user for the type of dungeon they would like

to explore. In this assignment, there is only one type of dungeon: a “basic” dungeon. Refer to section Dungeon

(Rooms, Walls, Doors) for details on the types of dungeon and their construction.

Once the user selects a dungeon type, the game must create the first dungeon level according to the selected

type, place the character in the first room, display the room’s description, and prompt the user for the next

action. The user can take various actions depending on the configuration of the current room. The following

actions must be supported, as appropriate:

? Move the character to the next room: North, South, East, or West

? Move the character to the previous room (whether this is North, South, East, or West depends on the

door the user “came through”)

? Return to the main menu

In most cases, after a choice is made, the action menu is displayed again.

If the user chooses to move to another room, the current room will be updated to the room that was selected,

the new room’s description will be displayed, and the user prompted for the next action.

If the user chooses to return the main menu, the current dungeon and character will no longer be playable, i.e.,

a new character will have to be created. Therefore, the user must be warned that progress will be lost and

prompted for confirmation. If the user confirms their intent to return to the main menu, the number of dungeon

levels successfully completed must be displayed followed by the main menu.

Once the user reaches the final room of the dungeon the door to the next level will be revealed. If the user

chooses to go through that door (via the appropriate direction, North, South, East, or West), the game will return

the user to the main menu. If the user chooses to play the game (again), a new dungeon level must be built (of

the currently selected dungeon type) but the existing character will be used.

General Requirements

At any time a valid character exists, the user must be able to view their character details (stats, items, etc.). After

viewing the character details, the user will be able to view the weapon specific details, the item specific details, or

return to the main menu.

All user inputs must be validated. If an invalid input is given, a warning must be displayed to the user and the

menu options redisplayed.

Revision 2

Software Development with C++ - Assignment 1 Page 5 of 13

All classes must have appropriate constructors and destructors.

All objects that can be described must be able to be output to a stream using the stream output operator ‘<<’.

Player Character

The player character is represented by the Character class. This class will maintain the state regarding the

player’s character as well as define behaviours appropriate to the character (i.e., update the game state as

appropriate based on the character’s actions).

A character has a name (provided by the player), a combination of static properties and dynamic properties that

may affect the gameplay and the character’s actions, a weapon, and an item. When the player chooses the

option, the character’s stats must be displayed.

The properties are defined at character creation and include:

? Strength—allowable values 1-6, each value > 1 contributes to the damage of the character

? Dexterity—allowable values 1-6, each value > 1 contributes to the dodge chance of the character

? Wisdom—allowable values 1-6, each value > 1 contributes to the effects of magical items

The dynamic properties are derived from various sources and must be updated as appropriate. They include:

? Health points—starts at 50.

Character Creation

Before the game can be played, a character must be created by the user. Creating a character requires the user to

specify some parameters of their character. These include the name of the character and the allocation of points

to the static properties: Strength, Dexterity, and Wisdom.

The game will first prompt the user to enter the character’s name.

The game will then prompt the user to allocate the points the three static properties. The rules for allocating stats

are as follows:

? The user will start with 6 points to allocate

? The user will select the number of points to allocate to each property in turn: 1) Strength, 2) Dexterity, 3)

Wisdom.

? The selected number of points will be added to the base value of 1; the resulting value will be assigned to

that property of the Character object.

? The selected number of points cannot exceed 5, so that the total value of the property does not increase

beyond the maximum of 6 points.

? Zero points can be selected if desired, in which case the value of the property will remain the base value.

? After the user selects a valid number of points, the selected number will be subtracted from the number

of available points.

? When selecting the number of points to assign to subsequent properties, the current number of available

points cannot be exceeded.

? After the user has selected the number of points to assign to each property there should be no more

points available. If there are remaining points to be allocated, the game must prompt the user to confirm

that they did not want to assign all the points. If the user indicates that they wish to continue assigning

points, the game will go back to assigning points to each property in turn with the current available

points.

? The game must not allow the value of a static property to exceed the maximum value of 6.

Revision 2

Software Development with C++ - Assignment 1 Page 6 of 13

After the user has allocated all points, or indicated that they do not want to assign the remaining points, the

character description and stats must be displayed to the user. This is the same description that would be

displayed if the user chose to display their character details from the menu.

Character Details Display

When displaying the character details, the following must be included:

? The character’s name

? Strength

? Dexterity

? Wisdom

? Health: current and maximum

After displaying the character details, the menu will allow the user to return to the main menu. These options will

not be available when the character details are being displayed for the first time immediately following character

creation.

Dungeon (Rooms, Walls, Doors)

A dungeon level consists of a connected set of rooms and are to be represented by the Dungeon class. Each room

of a dungeon (represented by the Room class) must be identifiable by a number (integer) so that they can be

retrieved from the dungeon object using the numeric identifier. Each room has 4 directions: North, South, East,

and West. In each of these directions, there may be a wall (the Wall class) or a door (the Door class). Doors are

connected to adjacent rooms via a door in the opposing direction. For example, if a room has a door to the north,

the northern room has a door to the south.

Doors are connected to each other to allow movement between rooms in both directions. There are two

exceptions: the entrance to the dungeon level, and the exit. When the user chooses to go through the dungeon

entrance, they will be returned to the main menu as if they chose the option to return to the main menu, that is

they will lose their progress and the dungeon and character will have to be recreated according to the General

Gameplay section. When the user chooses to go through the exit door, they will be returned to the main menu

ready to continue to the next dungeon level as described in the General Gameplay section.

Door objects must be connected to one another using bare pointers in C++.

When the user enters a room, a description of that room must be provided to the user. The description includes

what the room “looks like” (this is up to you, or you can use the text of the examples) and what the character

“sees”, that is, where the doors are located. Descriptions of walls may be included.

When a dungeon level is being created, various elements must be selected randomly including: the type of room.

The size of the dungeon (i.e., the number of rooms) and the connections between rooms will not be randomly

generated. When making a random selection, a simple even weighting between choices can be used. Moreover, it

is not required to do any complex registration of types: a simple switch statement will be acceptable.

Dungeon Types

You must implement one dungeon type: a basic dungeon (BasicDungeon class). Each dungeon type can contain

different types of rooms, walls, and doors.

The basic dungeon is built out of the following:

? Rock room—A dark and empty chamber (Room type)

? Quartz room—The chamber glitters like a thousand stars in the torchlight (Room type)

Revision 2

Software Development with C++ - Assignment 1 Page 7 of 13

? Basic Wall—Out of the darkness looms a jagged formation of rock: you cannot go that way (Wall type)

? Open Doorway—… you see an opening to another chamber (Door type)

The Standard Dungeon Level

To simplify the assignment and make it easier to test and mark, you are not required to implement randomly

generated dungeon levels. Some elements of the dungeon are still randomised, for example, the type of each

room. The structure of the (first) dungeon level must conform to the standard structure defined below. There is

no constraint on the structure of dungeon levels after the first.

In the layout diagram the following symbols are used:

? Large green rectangles represent rooms with the numbers indicating their ID

? Small blue bars joining rooms represent connected pairs of doors

? Entrance and Exit are labelled and indicated by a directional arrow

? Orange smiley faces represent creatures (irreleveant for assignment 1)

? Yellow suns represent consumable items or weapons (irreleveant for assignment 1)

Menu

The basic menu structure will be provided for you, but you will need to implement the interactions with the Game

and the transitions between menus for the different contexts, for example, the main menu to the action menu

while playing the game. You are quite free to implement the menu as you desire, that is, the format and text

descriptions of the menu does not have to exactly match the output examples provided. However, the values

entered to perform specific functions must match the following specification to ensure consistency and support

testing/marking. Note: it should not matter if the typed character is uppercase or lowercase.

If in doubt, refer to the provided ‘input script’, which is a series of text input which can be fed automatically to the

application to test the game, and the reference implementation executable.

Revision 2

Software Development with C++ - Assignment 1 Page 8 of 13

Command Character Command Character Command Character

Main Menu Dungeon Selection Menu Action Menu

Play game p Basic Dungeon b Go North n

Character

Details

c Magical

Dungeon

m Go East e

Quit q Go back/Return r Go South s

Character Details Menu Combat Menu Go West w

Weapon Details w Go back b Pick-up Item p

Item Details i Attack a Compare Items o

Previous Menu m Use Item i Use Item i

Use Special

Ability

l Use Special

Ability

l

Character

Details

c Character

Details

c

Main Menu m Main Menu m

Note: Menu commands that are not relevant for assignment 1 have been crossed out.

Documentation

Your code must be documented appropriately using Doxygen comments. Comment blocks must be used in your

header file to document your classes and class members. Private members should also be documented when it is

not obvious for what they are used—but first check if the code can be improved as clean code is better than

comments. Use comments sparingly in source files, document blocks of code (switch statements, if else groups,

loops, etc.) rather than individual statements: do not comment individual statements, unless the outcome of the

statement is not obvious. Optionally, you can add comments to ‘main.cpp’ to document the ‘main page’.

Code Style

You must write you code conforming to the code style set for this course.

See http://codetips.dpwlabs.com/style-guide

Implementation Rules and Hints

Your initial UML class design may be incomplete or incorrect. Your first design should attempt to cover the major

requirements, in particular the identification of the 2 design patterns. Do not worry if you need to revise and

refactor the design as you progress through the assignment.

It is a good idea to commit the source file (but not the PNG or PDF) of your UML class design to version control.

This will allow you to track the evolution of your design as you progress through the assignment.

It is recommended that you test individual components as you go, rather than trying to test the whole application

through the menu-driven interface. Writing code to test specific elements will speed up development as you will

not need to constantly enter data through the menu interface.

Be sure to check for null pointers before attempting to use an object through a pointer and reset pointers to null

where appropriate.

The game will need to keep track of (at least) the current room, the character, and the previous room/door.

Revision 2

Software Development with C++ - Assignment 1 Page 9 of 13

Ensure you have considered similarity between classes so that you can achieve reuse through inheritance where

appropriate. Also, remember to program to an interface not and an implementation. For example, if you have

related classes but only some have particular data members and you want to ensure substitutability, you can

define an interface on a base class using virtual functions that the derived classes then override appropriately.

The derived classes that do not have those particular data members may then rely on a suitable default

implementation that does not use those data members.

Consider when and where it is appropriate to use smart pointers vs. bare pointers and the effect this choice may

have on constructors, destructors, etc. In most cases the choice is up to you with the exception of the connection

between two doors, doors must be connected via bare pointers.

Smart Pointers – the Dungeon class uses the smart pointer std::shared_ptr<Room> to store and retrieve the

rooms (among other things). You must use the std::make_shared<DerivedType>() template function to create

the rooms to pass to the function Dungeon::addRoom. The below code snippet is an example of adding a room to

a dungeon:

Dungeon &d = Dungeon{};

d.addRoom(std::make_shared<RockChamber>(/* constructor arguments go here */));

The Dungeon and Room classes may not be the only occurrence of smart pointers in your implementation.

Casts – in your code, casts should only need to be performed between a base class pointer and a derived class.

When doing so, a dynamic cast should be performed (using dynamic_pointer_cast<Type>). For example, to cast

an Item pointer to a Weapon pointer you would use the following code snippet:

std::shared_ptr<Item> item = …; // Wherever the value comes from

std::shared_ptr<Weapon> weapon = std::dynamic_pointer_cast<Weapon>(item);

Type defines – if you know what you are doing, you may define type aliases for the shared pointer types for use in

the MenuInterface and your classes.

Revision 2

Software Development with C++ - Assignment 1 Page 10 of 13

Submission Details

You must submit your complete Qt Creator project folder inside a single zip file. The zip file must include all

source files required to compile and run your program (including a Doxygen config file) as well as your version

control directory: e.g., if using git you must ensure the ‘.git’ folder is present in the zip file. It must not include any

generated or extraneous files such as the ‘*.pro.user’ file—the UML image file is the one exception to the no

generated files rule. Place your UML class diagram in your zip file. Your diagram must be submitted in PNG or PDF

format.

Once you have created the zip file according to the specifications, you are required to upload it to the Assessment

Item 1: Assignment submission via CloudCampus.

Marking Criteria

Your code will be inspected for style – remember consistency is the primary goal of all style guides, the easier it is

to understand your code, the easier it is to allocate marks.

Note: the criteria with negative marks are penalties that will be applied if the submission does not meet the

expected criteria. They are structured such that an otherwise perfect submission will be unable to receive an HD if

and only if the full penalties are applied.

Criteria Mark

Correct identification and use of the design patterns: —

? Singleton 10

? Builder 10

Correct use of inheritance 10

Correct separation of code in header and source files: declaration vs. definition 5

Correct implementation of classes: constructors/destructors, data members, member functions, public/private

accessibility

10

Correct use of (bare) pointers and smart pointers 10

Correct implementation of functional requirements: —

? General gameplay 15

? Player Character 15

? Dungeon Creation 10

? Menu Interface 5

Inappropriate use of version control (-5)

Source code does not follow style guide (-5)

Inadequate/inappropriate use of comments (Doxygen) (-5)

Total Possible Marks 100

Revision 2

Software Development with C++ - Assignment 1 Page 11 of 13

Appendix 1: Design Patterns

In developing the class design for the Dungeon Crawler game, the following the Design Patterns must be

incorporated:

? Singleton

? Builder

This section provides brief descriptions of each relevant design pattern in turn. For more information refer to the

book from which these summaries are derived:

Gamma, E, Helm, R, Johnson, R and Vlissides, J 1995, Design patterns: elements of reusable object-oriented

software, Addison-Wesley, ISBN: 978-0-201-63361-0. Be aware that the C++ examples from the book are for an

older version of C++. If using them for guidance, they must be updated to C++ 14.

Adequate descriptions of the above patterns along with some code examples can also be found on Wikipedia:

? Singleton: https://en.wikipedia.org/wiki/Singleton_pattern

? Builder: https://en.wikipedia.org/wiki/Builder_pattern

Singleton

The Singleton pattern addresses the problem of ensuring that a class has only one instance. Moreover, the one

instance is easily accessible but in a controlled fashion. Such a pattern arises in situations where a single, globally

accessible interface to a system or subsystem is required and, hence, is often used with other patterns such as

Fa?ade and Abstract Factory. An example of singleton a singleton is an application wide configuration. Rather

than parsing the configuration file multiple times and storing multiple copies in memory, the configuration file

should be read once and stored once. Moreover, since various parts of the application may need to access

different settings, the configuration should be globally accessible to ensure the configuration is accessible when

needed. Other examples include: a single print spooler governing access to the printer resources, a single

thread/connection pool handing out connections or threads to clients that request them, etc.

When to Use

The Singleton pattern should be applied to a class when:

1. Exactly one instance of the class must exist

2. A well-known access point to the instance is required

In general, the extensibility of a Singleton through subclassing should also be considered. It will not be required

for this assignment; however, it is important to note as a common criticism of the Singleton pattern is that it

becomes tied to implementation and makes testing difficult due to the inability to replace the singleton instance

with a mock (i.e., a test specific class that is not a complete implementation).

General Structure

A simple representation of the Singleton pattern includes only a Singleton class, i.e., the class that should be a

Singleton, containing: a static member function instance() that returns the unique instance of the class, a

hidden constructor, and a static data member to hold the unique instance of the class.

Singleton

+ static instance()

- Singleton()

- static theInstance

return theInstance;

Revision 2

Software Development with C++ - Assignment 1 Page 12 of 13

Note the ‘+’ indicates public accessibility and ‘-‘ indicates private accessibility. If considering extensibility of the

Singleton through subclassing, the constructor should be protected rather than private.

Implementation Hints

In C++, the compiler may generate some constructors and operators for you. Ensure you prevent the Singleton

from accidentally being copied or assigned in ways you do not intend by deleting any automatically generated

members that you do not need.

In C++, the static data member can be implemented in two different ways: within the scope of the class or within

the scope of the function. Consider the consequences of each approach and justify your implementation choice in

your comments.

Builder

The purpose of the Builder pattern is to separate the representation of a complex object from the process used to

construct it. Further, such separation allows different representations to be constructed from the same process.

For example, a Parser using a Builder to construct a parse tree in different systems: the recognition of the syntax

is handled by the parser, which calls the builder to create the appropriate parse nodes and retrieves the final

parse tree from the Builder once the parsing is complete. The different representations may or may not be

related, for example, an executable parse tree of the parsed syntax, a parse tree for another language (such as in

code transformation), or a composition of widgets for visualising the text processed by the Parser.

An important aspect of the Builder pattern that differentiates it from other creational patterns is that it supports

the creation of complex objects in a step-by-step process, rather than all-at-once.

When to Use

The Builder pattern should be applied when:

? the construction process is, or should be, independent of the object, its parts, and the way that it is

assembled

? the construction process should allow for different representations to be created.

Note that there does not always have to be multiple representations, only that multiple representations are

possible. One of the motivations for good design is to support future change more easily.

General Structure

The Builder pattern contains several interacting classes, including:

? the Director, which represents the entity that requires the object to be constructed and ‘directs’ the

Builder to construct it;

? the Builder, which specifies an abstract interface for constructing any Product object, this may include

many different member functions for constructing different types of parts;

Revision 2

Software Development with C++ - Assignment 1 Page 13 of 13

? one or more ConcreteBuilders, which create specific Products from its parts by implementing the

abstract Builder interface and allows the Product to be retrieved once complete as it maintains the

representation internally during construction;

? the Product class, which represents the complex object being created by a ConcreteBuilder as well as

its parts. Note: this does not mean the classes for the parts are encapsulated by the Product class, only

that the diagram is simplified to not explicitly illustrate any of the Product’s parts.

Implementation Hints

If the representations being constructed by the different ConcreteBuilders are related, the abstract Builder

may declare an appropriate getResult() member function as part of its interface.

The ConcreteBuilder object is not required to be supplied to the Director via a constructor, it could also be a

parameter to a member function.


版权所有:编程辅导网 2021 All Rights Reserved 联系方式:QQ:99515681 微信:codinghelp 电子信箱:99515681@qq.com
免责声明:本站部分内容从网络整理而来,只供参考!如有版权问题可联系本站删除。 站长地图

python代写
微信客服:codinghelp