![]() |
|
#1
|
|||
|
|||
Tic Tac Toe Help! Error 2059 in C++Hey I got an error in my syntax. I've checked this over a couple of times now, and still do not understand what I am doing wrong. Please help!
(line 152) : error C2059: syntax error : '}' CPP / C++ / C Code:
|
|||
|
#2
|
|||
|
|||
Re: Tic Tac Toe Help! Error 2059 in C++There is no 'while' condition in your outer do-while loop. main() must return an int, though your compiler isn't so picky about it apparently.
Edit: You probably didn't mean to put an endless while loop at line 74. Either it should be the start of a while or the end of a do-while loop. |
|
#3
|
|||
|
|||
Re: Tic Tac Toe Help! Error 2059 in C++Oh wow, my bad I completely missed that. Thanks!
|
|
#4
|
||||
|
||||
Re: Tic Tac Toe Help! Error 2059 in C++Quote:
I don't know if this will be of much help to you, but perhaps a few comments about your code and an example of an alternative implementation may be useful to you? One thing that is apparent is that your code is not valid C++ by today's standards. You must declare and implement your main function such that it returns an int as Kimmo points out. The other thing is that your code is "monolithic," which means (at least in this case) that you are doing everything in one function. Maybe you haven't learned how to use supporting functions yet or perhaps you're not allowed to use them (or other language features) yet? But it makes your code and your development process very difficult to understand and debug. It also probably contributes greatly to writing more buggy code. What we tend to do is use separate functions to do bits and pieces of the "work" needing done by the intent of the program. This piecemeal approach to separating the work into smaller, more manageable chunks is sometimes called "divide and conquer," though that paradigm is sometimes "reserved" for recursion, but inaccurately so IMO. The basic notion of what I'm referring to as being divide and conquer is to define your data "structures" and your "algorithms" that work on the data contained within your "structures" such that you modify the data contained inside by each functiion that knows how to do one aspect of your programming objective. In my "alternative implementation," which is intentionally "less than perfect," I'll demonstrate some various features of the language (both "good" and "bad") to help you understand how to separate your "work." The functions that I decided were necessary for my implementation are: draw_board reset_board win_check_board who_goes_first play ...and, of course: main The purpose and "work" that these different functions do is fairly apparent already just by their names. One of the "challenges" programmers face is coming up with meaningful names that help express their intent and design objectives so that their code is more readily understood by others and themselves. "main" is, as we know, the entry point to the application, and as such, its name isn't very expressive of the intended objective of the work that we need or want to do. Therefore, it is nice if one can have main do the minimalist set of work needed to get us going so that we can be more expressive by using functions whose names lend more meaningful insight as to the intent. In a way, we can think of main as a "necessary evil" but also as an opportunity to begin using descriptive naming as quickly as possible. The very first thing that I did was to design a tic-tac-toe "board" using a simple text editor. Here is what I came up with: Code:
This was helpful for a few different reasons. It was so helpful that I cut-n-pasted it into my code and used multi-line comments to remind myself of the layout of the board. To me and my design, this is kind of the roadmap of what I wanted to see and what I needed to model in code so that the play would proceed in a useful fashion, but also so that I could debug my implementation along the way. What became clear to me was that I had 9 relevant "data points." Something also was that if one of these data points was anything BUT an X or an O, then two things of importance were apparent: 1: The position is available for writing either an X or an O 2: The "user feedback" of "menu choice" for selecting the position to play was "self-contained" in the "presentation layer." That is, that after some turns of play, we would end up with something like: Code:
...here we can easily see which positions are available for play. The user, regardless of whether playing X or O has only to enter the number that corresponds to an empty position. So, in my implementation, I could have added another function that sets up the game, but as I indicated previously, there are plenty of other "improvements" that can be made to this code to make it "better." Here's main: CPP / C++ / C Code:
As you can see in the code, we can assume that the call to who_does_first must query the user/player(s) to elect an initial player, in this case, either player X or player O. What is a bit strange is immediately after electing who goes first, I select the opposite player and call "play." This kind of design is confusing to readers of the code. The users of the game couldn't care less, because they do not see the implementation of it or have any sense that I'm calling "play" with the "previous" player being passed in as an argument to play. However, the "reason" for this is because play is a recursive function. But, I'm getting ahead of myself, right? Here is who_goes_first: CPP / C++ / C Code:
Here I mindlessly continue to query the user for what I'm willing to consider is valid input. That is, I am demanding that the user give me something that enables me to decide whether X will go first or whether O will play first. Note that I will accept either uppercase or lowercase input, because the case is not important for me to decide who goes first, but also note that elsewhere in the program, I work only with uppercase X or O, because that information is important to my "presentation layer," which has the responsibility of giving the user feedback essential to drawing the board in a consistent fashion and is used in checking to see if a position is already taken by a previous turn. Few users will "mind" having to select either an X or and O, because we can assume that most users would already have the basics of playing the game fairly well understood. Therefore, the "inconvenience" is minimal, but the "protection" of a miskey during input is adequately provided. Internally, we don't care who plays first, but users tend to care as they relate the mark to their play and therefore their win/loss/tie. We don't care if the same user plays both marks nor do we care if the result of the game is a win/loss/draw for X and/or O. We only want to report the result of play accurately and ensure that the play is fairly conducted. Since main uses the result of who_goes_first and then picks the opposite player to "begin" play, we probably would want to "refactor" the code so that we take out this "contradiction" in logic. However, I present it here for review of both "good" and "bad" practices. Some of these I'll point out. Others are left for others to review and/or offer remedies for as they may or may not choose. So, we're onto "play:" CPP / C++ / C Code:
"play" does a lot of "work" The most notable thing is that it immediately calls "draw_board" upon entry. As we can easily guess, draw_board will present the state of the game after any modification or whenever it is deemed useful by the program to represent the board (and its state) to the user. draw_board only pushes the state information onto the screen. It doesn't accept user input, it doesn't validate that any of the content contained in the state is meaningful or correct/incorrect, it just dumps to screen in a predetermined fashion. CPP / C++ / C Code:
What we can immediately see in draw_board is that it relies on some "missing pieces" that are not defined in its own function block. ROWS and COLS are quickly apparent and then we have "positions" whatever that may be. I'll focus solely on "positions" and let any others discuss the remainder of the implementation. "positions" is the "data structure" of the 9 elements of relevant data that I decided was adequate in describing the state of play at any given time. While the true state of play would also include things like "who should play next" so that the "data structure" fully is descriptive of the state of play, my design chooses to ignore that information in the data structure. That one piece of information could be added to a redesigned data structure, but it would require a relatively significant "rewrite" of my implementation. It would, however, "clean-up" the implementation significantly, too. We wouldn't have to pass "last_player" around to play and, perhaps more importantly, the data structure wouldn't have to be "global" to the application. While arguable "ok" for this implementation, if we wanted to extend this application such that the computer could play against a human player or even against itself, we'd more quickly find the limitations of this design. The thought to be taken from this is that as program complexity increases, the use of global data becomes more difficult to safely manage. For at least this reason, global data is considered to be "bad," and it certainly can be very bad, depending on what the future brings to our application. For some applications, extending them becomes too tedious of a chore because of the misuse of global data and/or the limitations of the data structures and therefore the implementations of those "worker" functions that perform work on them. Is is the case with nearly everything, there is "good" and "bad" in all of it. Choosing the lesser-of evils is often a reality that I like to think of as an excellent opportunity for a code comment. Obviously, this code contains no real commenting to describe why/what/how or any of the thinking that went into its design and/or implementation. That alone makes the code "bad" in my eyes. Sometimes a body of code will be "incredibly obvioius" and perhaps that should be a goal every programmer sets, but it is too idealistic to think that it is reasonably feasible for every piece of code or that programming tasks are so well forethought so as to be much more than a moment where the programmer arrived at the desired result and stopped improving the code. Obviously there is a business case (both for and against) to be made for that approach, too. Here is "positions" CPP / C++ / C Code:
As previously seen by the "board layout," we can easily see how each character represents either an available position (if not an X or an O) to play (regardless of whether that play makes good sense to winning the game) and it represents the "state" of the board as we make changes to the values contained in the array. It may have been better to code it as follows: CPP / C++ / C Code:
...now it visually appears more like our board. Now it is easy to see how each "number" represents an "open position" and if we change one of the positions (hence the name of the data structure) to an X or an O, we can easily "draw" it to screen in a consistent way, but also, we can easily visualize the interactions during our implementation efforts. Something a bit less obvious about using this "data structure" is that it also happens to be helpful in validating our user input because its size nicely corresponds to the largest "value" entered by the user. If the user enters something higher than the number of positions available, we'll know immediately that the input is invalid. As we look at "play" we can see that this is accommodated, but could use some refinement. Here is the relevant code: CPP / C++ / C Code:
Note that there is an evil bug lurking in this code. It is something of a "timebomb" just waiting for the "wrong" user input to set it off. It will also illicit undefined behavior (or, implementation-specific if you prefer). "undefined behavior" is definitely "bad." At the very least, it suggests that the code is "not ready for primetime" and therefore warrants further review. I'll leave it open for discussion among those willing to participate. And so, it brings us to the play(last_player) line that says, if we can't get it right, we'll do it again, telling the play function who played last so that we can have the "same user" play again, but hopefully "better" with respect to our desired input constraints. As you can see, there are several areas where opportunities for confusion creep into the design. Combine it with a goto and "sharing the responsibility" of "managing" future play within itself (play again?), we further "convolute" the function while "mixing" into it various roles of data input, data validation, game replay management, deciding when/if to check for a winning board position and more. These kinds of things often come up in the course of even a normal work day for a programmer. We often "wander" over to the "dark side," only to have to "refactor" our ways back into the light. It isn't until we've had a chance to "do a little, test a little, debug a little and refactor a little" that we "arrive" at something that works as intended and doesn't "violate" our sense of "what's right" while being logical, sensical and presented in an inviting manner for others to critique. When we see "really bad" code, where do we start with offering advice? Who wants to spend a month of Sundays describing "all of the ways" that the code needs improvement and who wants to bear the burden of an expose on "all of the ways" that the code figuratively "reeks." Finally, here is the entire code for your review of what is both "good" and "bad" in it. There are likely equal parts of both: CPP / C++ / C Code:
...some of the bugs are fun to find, but may be perhaps more fun without any more clues. MxB |
Recent GIDBlog
Not selected for officer school by crystalattice
| Thread Tools | Search this Thread |
| Rate This Thread | |
|
|
Network Sites: GIDNetwork · GIDWebHosts · GIDSearch · Learning Journal by J de Silva, The