Bài 1.7: Làm việc với hằng số (Constants) Hằng số là một giá trị không thay đổi mà tên của nó do bạn đặt. Hằng số rất hữu dụng nếu bạn có một giá trị không thay đổi và nó xuất hiện thường xuyên trong chương trình của bạn. Ví dụ, nếu bạn viết trò chơi bắn máy bay, mỗi kẻ địch có giá trị điểm là 150, bạn nên đặt một hằng số tên ALIEN_POINTS có giá trị là 150. Sau đó, bất cứ khi nào bạn cần giá trị đó, bạn có thể dùng ALIEN_POINTS thay cho giá trị 150. Hằng số có hai lợi ích. Thứ nhất, nó làm cho chương trình trong sáng dễ đọc hơn. Ngay khi bạn thấy ALIEN_POINTS, bạn đã biết nó có ý nghĩa gì. Nếu bạn bất chợt nhìn vào một đoạn mã và thấy số 150, bạn có thể sẽ không biết giá trị đó đề cập đến cái gì. Lợi ích thứ hai, dùng tên hằng như vậy sẽ rất thuận tiện khi bạn muốn thay đổi giá trị của nó. Ví dụ nhé, chẳng hạn bạn muốn thử nghiệm trò chơi của mình và bạn quyết định tăng giá trị của kẻ địch (hyutars: alien = người ngoài hành tinh, dịch kẻ địch cũng được ^^) lên 250. Với hằng số, tất cả những gì bạn phải làm chỉ là thay đổi dòng khởi tạo ALIEN_POINTS thành 250. Nếu không dùng hằng số, bạn sẽ phải tìm và thay đổi từng số 150 thành 250, nếu đoạn mã của bạn khoảng 1000 dòng thì … chậc chậc >_< … Giới thiệu chương trình Game Stats 3.0: Chương trình Game Stats 3.0 dùng hằng số thay cho giá trị. Đầu tiên chương trình tính điểm của người chơi, và sau đó nó tính mức phí cho việc nâng cấp bộ phận nào đó trong một game chiến thuật. Hình 1.8 cho thấy kết quả: Hình 1.8: Mỗi phép tính đều có sự tham gia của hằng số, nó làm cho đoạn code rõ ràng hơn. Mã: // Game Stats 3.0 // Demonstrates constants #include <iostream> using namespace std; int main() { const int ALIEN_POINTS = 150; int aliensKilled = 10; int score = aliensKilled * ALIEN_POINTS; cout << "score: " << score << endl; enum difficulty {NOVICE, EASY, NORMAL, HARD, UNBEATABLE}; difficulty myDifficulty = EASY; enum ship {FIGHTER = 25, BOMBER, CRUISER = 50, DESTROYER = 100}; ship myShip = BOMBER; cout << "\nTo upgrade my ship to a Cruiser will cost " << (CRUISER - myShip) << " Resource Points.\n"; return 0; } Dùng Hằng số: Tôi định nghĩa hằng số, ALIEN_POINTS, để nó nhận giá trị của một kẻ địch: Mã: const int ALIEN_POINTS = 150; Tôi dùng keyword const để định nghĩa hằng số. Bây giờ tôi có thể dùng ALIEN_POINTS như một số nguyên bình thường. Bạn có thể để ý thấy, tên tôi chọn cho hằng số toàn là chữ hoa. Đó là một thói quen, nhưng rất phổ biến. Tên được đặt toàn bằng kí tự hoa nói với lập trình viên khác rằng: nó là một hằng số. Tiếp theo tôi dùng hằng số trong dòng này: Mã: int score = aliensKilled * ALIEN_POINTS; Tôi tính điểm của người chơi bằng cách nhân số kẻ địch đã tiêu diệt với giá trị điểm của một kẻ địch. Dùng hằng số ở đây giúp cho dòng mã khá rõ ràng. Bẫy: Bạn không thể dùng phép gán một giá trị mới cho một hằng số. Nếu bạn thử, lỗi sẽ phát sinh. Dùng Enumerations (Bảng liệt kê): Enumeration là tập hợp của các hằng số kiểu unsigned int, gọi là enumerator (Phần tử liệt kê). Thường thì enumerator liên quan với nhau và có một thứ tự nhất định. Sau đây là một ví dụ của enumeration: Mã: enum difficulty {NOVICE, EASY, NORMAL, HARD, UNBEATABLE}; Nó định nghĩa một enumeration tên difficulty. Mặc định, giá trị của enumerator đầu tiên là 0 và các giá trị sau tăng lên 1. Ở dòng code trên, NOVICE là 0, EASY là 1, NORMAL là 2, HARD là 3, và UNBEATABLE là 4. Để định nghĩa một enumeration, dùng keyword enum, sau đó là tên của nó, tiếp theo là danh sách các enumerator giữa hai dấu { }. Tiếp theo tôi tạo một biến, kiểu của nó là enumeration tôi đã tạo. Mã: difficulty myDifficulty = EASY; Biến myDifficulty được đặt là EASY (có giá trị = 1). myDifficulty thuộc kiểu difficulty, cho nên nó chỉ có thể giữ một trong các giá trị mà bạn đã định nghĩa trong enumeration. Nó có nghĩa là, myDifficulty chỉ có thể được gán các giá trị NOVICE, EASY, NORMAL, HARD, UNBEATABLE, 0, 1, 2, 3, hay 4. Tiếp theo tôi định nghĩa một enumeration khác: Mã: enum ship {FIGHTER = 25, BOMBER, CRUISER = 50, DESTROYER = 100}; Dòng code này định nghĩa enumeration ship, nó đề cập đến 4 loại ship (tàu) trong game chiến thuật. Trong nó, tôi gán các giá trị kiểu nguyên cho một số enumerator. Các chữ số là giá trị Resource Point (tài nguyên) của mỗi ship. Bạn có thể gán giá trị cho enumerator nếu bạn muốn. Bất kì enumerator nào không được gán giá trị sẽ nhận giá trị của enumerator đứng trước nó cộng thêm 1. Vì tôi không gán giá trị cho BOMBER, nó sẽ mang giá trị 26. Tiếp theo tôi định nghĩa một biến kiểu enumeration: Mã: ship myShip = BOMBER; Sau đó tôi mô tả cách thức dùng enumerator trong tính toán số học: Mã: (CRUISER - myShip) Code trên có chức năng tính mức phí để nâng cấp từ BOMBER lên CRUISE. Phép tính này tương đương 50 – 26 = 24.
Bài 1.8: Giới thiệu chương trình LOST FORTUNE Project cuối cùng trong chương này, Lost Fortune (Kho báu bị mất), là một đoạn mã nhận dạng người chơi thường thấy trong các game nhập vai, người chơi sẽ nhập vào thông tin của mình (gồm cả tên), sau đó máy tính sẽ dùng nó in ra màn hình một cốt truyện. Hình 1.9 cho thấy kết quả: Hình 1.9: Câu chuyện được xâu dựng dựa trên thông tin người chơi cung cấp. Thay vì giới thiệu cả đoạn code một lần, tôi sẽ đi vào từng phần nhỏ. Cài đặt chương trình: Đầu tiên tôi viết vài câu chú thích, sau đó include hai file thư viện, và viết vài chỉ thị: Mã: // Lost Fortune // A personalized adventure #include <iostream> #include <string> using std::cout; using std::cin; using std::endl; using std::string; Tôi include file string, một phần của thư viện chuẩn, để tôi có thể dùng các đối tượng trong string (chuỗi) để truy cập đến chuỗi thông qua biến (hơi rối nhưng đọc đoạn mã dưới bạn sẽ hiểu). Có rất nhiều đối tượng trong string, nhưng các bạn đừng vội. Bạn sẽ học kĩ về chúng trong chương 3: “Vòng lặp For, Chuỗi, và mảng: Word Jumble.”. Tôi dùng chỉ thị để giải thích các đối tượng trong namespace std mà tôi định truy cập. Bạn có thể thấy string trong namespace std. Lấy thông tin từ người chơi: Tiếp theo tôi lấy một số thông tin từ người chơi: Mã: int main() { const int GOLD_PIECES = 900; int adventurers, killed, survivors; string leader; //get the information cout << "Welcome to Lost Fortune\n\n"; cout << "Please enter the following for your personalized adventure\n"; cout << "Enter a number: "; cin >> adventurers; cout << "Enter a number, smaller than the first: "; cin >> killed; survivors = adventurers - killed; cout << "Enter your name: "; cin >> leader; GOLD_PIECES là một hằng số có chức năng lưu giữ số vàng mà người chơi tìm ra. Biến adventurers lưu giữ số người phiểu lưu trong câu chuyện. Biến killed lưu giữ số người chết trong chuyến phiêu lưu. Tôi tính biến survivors cho số người còn sống sót. Cuối cùng, tôi lấy thông tin về tên của người chơi, người mà tôi truy xuất thông qua biến leader. Bẫy: Nếu bạn dùng cin để lấy chuỗi từ người chơi, bạn chỉ có thể lấy chuỗi không có kí tự trắng (như phím space hoặc tab). Có cách để giải quyết vấn đề này, nhưng nó cần phải bàn tới một thứ gọi là streams, có lẽ nó vượt quá tầm kiến thức của chương này. Cho nên, tạm thời bạn cứ dùng cin, và nhớ phạm vi hoạt động của nó trong việc lấy chuỗi. Kể chuyện: Tiếp theo tôi chèn các biến để kể câu chuyện này: Mã: //tell the story cout << "\nA brave group of " << adventurers << " set out on a quest "; cout << "-- in search of the lost treasure of the Ancient Dwarves. "; cout << "The group was led by that legendary rogue, " << leader << ".\n"; cout << "\nAlong the way, a band of marauding ogres ambushed the party. "; cout << "All fought bravely under the command of " << leader; cout << ", and the ogres were defeated, but at a cost. "; cout << "Of the adventurers, " << killed << " were vanquished, "; cout << "leaving just " << survivors << " in the group.\n"; cout << "\nThe party was about to give up all hope. "; cout << "But while laying the deceased to rest, "; cout << "they stumbled upon the buried fortune. "; cout << "So the adventurers split " << GOLD_PIECES << " gold pieces."; cout << leader << " held on to the extra " << (GOLD_PIECES % survivors); cout << " pieces to keep things fair of course.\n"; return 0; } Đoạn code khá rõ ràng. Tôi tính số lượng vàng mà người đứng đầu (leader) giữ, tôi dùng toán tử chia lấy dư trong biểu thức GOLD_PIECES % survivors. Biểu thức này tương đương với số dư của phép tính GOLD_PIECES / survivors. Người đứng đầu sẽ nhận thêm số dư này sau khi đã chia đều cho số người sống sót (kể cả người đứng đầu). (hyutars: Tới đây là xong rồi, có cần dịch câu chuyện không nhỉ … thôi dịch thoáng qua nhé, thực ra nó không quan trọng lắm. Đầu tiên nó bắt bạn nhập số người tham gia chuyến phiêu lưu, sau đó nhập một số bé hơn số vừa rồi (tức là số người chết), số còn sống sót là hiệu số của hai số trên. Câu chuyện thế này, một nhóm người tổ chức đi tìm kho báu bị mất, với dự dẫn đầu của leader – tên mà bạn nhập, sau đó họ gặp một toán ogre – quỷ nên giao chiến, chết một mớ, đúng lúc họ chán nản, nghỉ ngơi thì gặp kho báu, sau khi chia đều còn dư chút ít, thôi tui (leader) chịu khó nhận luôn ).
Tóm tắt: Trong chương 1, bạn đã học được các khái niệm sau đây: - C++ là ngôn ngữ lập trình bậc cao, nhanh, mạnh, được sử dụng phổ biến để lập trình games. - Chương trình là một dãy các câu lệnh trong C++. - Các yếu tố của ngôn ngữ C++ là ý tưởng, kế hoạch, mã nguồn, file object, thực thi. - Lỗi chương trình thường rơi vào các trường hợp: lỗi compile (biên dịch), lỗi link (liên kết), và lỗi run-time. - Chỉ thị #include ra lệnh bộ chọn trước include (bao gồm) một file vào file hiện hành. - Thư viện chuẩn là một tập hợp các file mà bạn include vào file chương trình để làm một số chức năng (hàm) gì đó. - Hàm (function) là một nhóm code có chức năng thực hiện công việc nào đó và sau đó trả về (return) một giá trị. - Mọi chương trình phải có hàm main(), đây là điểm bắt đầu của chương trình. - iostream - một phần của thư viện chuẩn, là một file chứa code giúp cho việc nhập xuất. - Namespace std chứa các công cụ từ thư viện chuẩn. Để truy cập đến các yếu tố từ namespace, bạn cần đặt tiền tố std:: ở phía trước hoặc dùng using. - cout là một object, định nghĩa trong file iostream, nó dùng để gửi dữ liệu đến lối ra (thường là màn hình máy tính). - cin là một object, định nghĩa trong file iostream, nó dùng để lấy dữ liệu từ lối vào (thường là bàn phím). - C++ được cài đặt sẵn các toán tử số học, như phép cộng, trừ, nhân, chia – và các toán tử không quen thuộc lắm nếu bạn chưa học lập trình . - C++ có các kiểu dữ liệu cơ bản là Boolean, char, integer, và double, float. - Thư viện chuẩn của C++ cung cấp object string dùng cho các chuỗi. - Bạn có thể dùng typedef để tạo tên mới cho kiểu dữ liệu đã có. - Hằng số là tên đại diện của một giá trị không đổi. - Enumeration là một chuỗi các hằng số kiểu unsigned int.
Hỏi Đáp: Hỏi: 1. Tại sao các công ty games dùng C++? 2. C++ khác C chỗ nào? 3. Tại sao tôi nên dùng chú thích (comments)? 4. Khối chương trình là gì? (Programming block) 5. Cảnh báo của trình biên dịch là gì? (compiler warning) 6. Tôi có thể bỏ qua compiler warnings được không? 7. Khoảng trắng (white space) là gì? 8. Tại sao hàm main() của chương trình trả lại giá trị kiểu int? 9. Literals là gì? 10. Tại sao tôi luôn nên khởi tạo một biến mới với một giá trị? 11. Tại sao lập trình viên nhiều khi dùng tên biến như myInt hay myFloat? 12. Biến kiểu bool dùng làm cái gì? 13. Tên “bool” bắt nguồn từ đâu? 14. Có phải bắt buộc đặt tên bằng chữ hoa cho các hằng số không? 15. Làm thế nào để tôi có thể lưu giữ nhiều hơn 1 kí tự trong một biến? Đáp: 1. C++ có tốc độ cao, truy cập được đến các phần cứng bậc thấp, nó hữu dụng hơn các ngôn ngữ khác. Thêm nữa, đa số các công ty games có rất nhiều resources (tài nguyên) của C++ (chẳng hạn như code có thể dùng lại được và kinh nghiệm của lập trình viên). 2. C++ là bản cải tiến của C. C++ có tất cả những gì mà C có. Như thế nào đi nữa, C++ cung cấp cho chúng ta những phương pháp mới để làm một công việc nào đó, thay thế các phương pháp cổ điển máy móc trong C. Thêm nữa, C++ cung cấp thêm khả năng viết chương trình theo hướng đối tượng. 3. Để giải thích cho các đoạn code khó hiểu. Bạn không nên chú thích những thứ đã quá rõ ràng. 4. Là một hay nhiều câu lệnh được bao gộp bởi hai dấu { } tạo thành một khối thống nhất. 5. Là một thông báo từ trình biên dịch của bạn về lỗi tiềm tàng. Warning sẽ không dừng quá trình biên dịch. 6. Bạn có thể, nhưng bạn không nên. Bạn nên tìm những warning và sửa nó. 7. Một tập hợp các kí tự không in ra được tạo nên các khoảng cách trong source file của bạn, bao gồm tabs, spaces, và xuống dòng. 8. Vì đó là cái chuẩn ISO khuyên làm. Trên thực tế, bạn có thể trả về một giá trị khác 0 để chương trình kết thúc, nhưng nó rất hiếm. 9. Yếu tố tái hiện lại các giá trị đã rõ ràng. “Game Over!” là literal string, trong khi 32 và 98.6 là literal số học. 10. Vì nội dung của các biến chưa được khởi tạo có thể là một giá trị nào đó – nó có thể ảnh hưởng đến chương trình của bạn. 11. Để cho thấy rõ ràng kiểu của biến. Thường thì cái này dùng thường xuyên trong các chương trình hướng dẫn. 12. Chúng tái hiện điều kiện đúng hoặc sai, như kho báu đã được mở hay chưa, card được úp hay mở. 13. Tên kiểu dữ liệu này được lấy từ tên của nhà toán học người Anh George Boole. 14. Không. Dùng chữ hoa chỉ là một thông lệ, nhưng bạn nên dùng vì đó là điều những lập trình viên khác mong đợi. 15. Dùng object string.
Câu hỏi thảo luận: 1. Những đặc điểm hữu ích của C++ giúp cho các lập trình viên? 2. Ưu điểm và khuyết điểm của việc dùng chỉ thị using? 3. Ưu điểm của việc định nghĩa một tên mới cho kiểu dữ liệu có sẵn? 4. Có hai cách viết (trước, sau) của toán từ tăng/giảm (++/--), sự khác biệt giữa chúng là gì? 5. Hằng số giúp ích cho chương trình của bạn như thế nào?
Trùi ui bị hư USB, mới mua cái mới để đem bài lên đây, dạo này học thi bị stress, còn một môn vào ngày 7 tháng 1 nữa ... tới lúc đó sẽ dịch thường xuyên hơn Chương 2: Truth (true & false), Nhánh, và Game Loop(vòng lặp) – Guess My Number Tổng quan: Cho đến bây giờ, các chương trình mà bạn đã thấy đều thuộc dạng tuyến tính – mỗi câu lệnh được thực hiện theo thứ tự từ trên xuống dưới. Nhưng, để tạo nên một game hấp dẫn, bạn cần phải viết được chương trình có khả năng thi hành (hoặc bỏ qua) một đoạn code căn cứ vào điều kiện nào đó. Đó chính là chủ đề chính của chương này. Cụ thể, bạn sẽ học: - Hiểu truth. - Dùng câu lệnh if để phân nhánh các đoạn code. - Dùng câu lệnh switch để chọn đoạn code cần thi hành. - Dùng vòng lặp while và do để lặp lại một đoạn code nào đó. - Tạo nên một số ngẫu nhiên.
Bài 2.1 Hiểu Truth Truth là trắng và đen. Bạn có thể tái hiện lại giá trị đúng và sai với từ khóa tương ứng với chúng, true và false. Bạn có thể lưu giá trị kiểu Boolean vào một biến kiểu bool, như bạn đã thấy ở chương 1. Ví dụ nè: Mã: bool fact = true, fiction = false; Đoạn code tạo nên hai biến kiểu bool, fact và fiction. fact là true (đúng) và fiction là false (sai). Bên cạnh từ khóa true và false dễ sử dụng, các biểu thức và giá trị cũng có thể thể hiện giá trị true và false. Các giá trị khác 0 sẽ thể hiện giá trị true, trong khi 0 sẽ thể hiện giá trị false. Biểu thức thể hiện lại giá trị true và false thường là biểu thức so sánh thứ gì đó. Sự so sánh được sử dụng khá thường xuyên với các toán tử liên quan. Bảng 2.1 sẽ cho chúng ta thấy vài toán tử và ví dụ của chúng. hyutars: Chốt lại, Truth là true or false, đúng hoặc sai. 0 là false, các giá trị khác 0 là true, biểu thức so sánh là rất thường dùng để rút ra giá trị true or false. Bài 2.2 Dùng câu lệnh if: Okay, bây giờ là lúc đem khái niệm true và false ra ứng dụng. Bạn có thể dùng câu lệnh if để kiểm tra xem biểu thức trả về giá trị true hay false và thi hành một đoạn code dựa vào đó. Sau đây là cú pháp đơn giản của câu lệnh if: Nếu biểu thức là true, sau đó câu lệnh sẽ được thi hành. Ngược lại, câu lệnh sẽ bị bỏ qua và chương trình rẽ nhánh đến câu lệnh phía sau khối lệnh if. (hyutars: Nếu đúng thì làm, nếu sai thì bỏ qua không làm) Lưu ý: Bất cứ khi nào bạn thấy từ “câu lệnh;” như trong cú pháp trên, bạn có thể thay nó bằng một câu lệnh đơn hoặc một khối lệnh (các câu lệnh ở giữa hai dấu { }). Giới thiệu chương trình Score Rater: Chương trình Score Rater đưa ra những lời bình luận về số điểm của người chơi – dùng câu lệnh if. Hình 2.1 cho thấy chương trình đang chạy. Hình 2.1 – Lời bình luận xuất hiện (hay không xuất hiện) dựa trên các câu lệnh if khác nhau. Mã: // Score Rater // Demonstrates the if statement #include <iostream> using namespace std; int main() { if (true) cout << "This is always displayed.\n\n"; if (false) cout << "This is never displayed.\n\n"; int score = 1000; if (score) cout << "Okay, at least you didn't score zero.\n\n"; if (score > 500) cout << "You scored over 500. Nice.\n\n"; if (score == 1000) { cout << "You scored a perfect 1000!\n"; cout << "Now that's impressive.\n"; } if (score > 500) { cout << "You scored at least 500.\n"; if (score >= 1000) cout << "You scored 1000 or more!\n"; } return 0; } Thử nghiệm true và false: Trong câu lệnh if đầu tiên tôi thử nghiệm giá trị true. Vì trong điều kiện (biểu thức) của câu lệnh if trả về giá trị true, chương trình sẽ hiển thị lời bình luận, This is always displayed (Dòng này luôn được hiển thị). Mã: if (true) cout << "This is always displayed.\n\n"; Trong câu lệnh if tiếp theo tôi thử giá trị false. Tất nhiên, do điều kiện của câu lệnh if trả về giá trị false, dòng bình luận sẽ không báo giờ xuất hiện, This is never displayed. Mã: if (false) cout << "This is never displayed.\n\n"; Bẫy: Chú ý bạn không dùng dấu chấm phẩy phía sau dòng if (…). Nếu bạn đặt dấu chấm phẩy phía sau nó, bạn sẽ tạo một câu lệnh if trống, nó sẽ không làm gì cả. Sau đây là ví dụ: Mã: if (false); cout << "This is never displayed.\n\n"; Khi bạn thêm dấu chấm phẩy phía sau if (false), câu lệnh phía trên sẽ tương đương: Mã: if (false) ; // tạo nên một câu lệnh trống, không làm gì cả cout << "This is never displayed.\n\n"; Tất cả những gì phía sau câu lệnh if (phần được thực thi nếu điều kiện đúng) chỉ là khoảng trắng. Vấn đề đã được làm rõ. Câu lệnh if có điều kiện false bỏ qua khối lệnh phía sau (khối lệnh chỉ có khoảng trắng). Sau đó chương trình tiếp tục con đường của nó, thực hiện code phía sau if, tức là nó sẽ hiển thị dòng This is never displayed. Hãy cẩn trọng với lỗi này. Nó rất khó phát hiện vì chương trình sẽ chẳng báo lỗi gì cả. Phiên dịch giá trị thành true hay false: Bạn có thể phiên dịch mọi giá trị thành true hay false. Tất cả các giá trị khác 0 được hiểu là true, trong khi 0 được hiểu là false. Tôi thử nghiệm lại trong câu lệnh if tiếp theo: Mã: if (score) cout << "Okay, at least you didn't score zero.\n\n"; score có giá trị 1000, vì nó khác 0 nên nó được hiểu là true. Kết quả, nó sẽ hiện thị lời thông báo Okay, at least you didn't score zero. Dùng các toán từ quan hệ: Có thể biểu thức mà bạn thường dùng nhất với câu lệnh if là biểu thức so sánh (dùng các toán tử quan hệ). Đó là những gì tôi minh họa tiếp theo. Đầu tiên, tôi thử nghiệm xem score có lớn hơn 500 không. Mã: if (score > 500) cout << "You scored over 500. Nice.\n\n"; Vì score lớn hơn 500, chương trình sẽ hiện thị lời bình luận, You scored over 500. Nice. Tiếp theo, tôi thử xem score có bằng 1000 không. Mã: if (score == 1000) { cout << "You scored a perfect 1000!\n"; cout << "Now that's impressive.\n"; } Vì score có giá trị bằng 1000, khối lệnh sẽ được thực hiện và cả hai chuỗi đều được hiển thị. Nếu score không phải 1000 thì không có dòng bình luận nào được hiển thị cả, và chương trình sẽ tiếp tục với các câu lệnh phía dưới khối lệnh. Bẫy: Toán từ quan hệ “bằng” là = = (hai dấu bằng trong một hàng). Đừng có nhầm nó với dấu = (toán từ gán). (hyutars: mình từng sai nhiều chỗ bởi cái lỗi này >_<). Chương trình sẽ không báo lỗi, vì nó chẳng làm gì sai cả, và kết quả có thể không phải là những gì bạn mong đợi. Hãy xem code này: Mã: int score = 500; if (score = 1000) cout << "You scored a perfect 1000!\n"; Ở kết quả của code này, score được gán thành 1000 và dòng bình luận, You scored a perfect 1000! sẽ được hiển thị. Sau đây là những gì đã xảy ra: Mặc dù score có giá trị 500 trước câu lệnh if, nhưng nó đã bị thay đổi. Chỗ (score = 1000) đã làm cho biến score được gán giá trị 1000. Biểu thức điều kiện của câu lệnh if lúc này trả về giá trị 1000, tức là giá trị khác 0, do đó nó tương ứng giá trị true. Kết quả, dòng bình luận được hiển thị. Cẩn thận với lỗi này, nó sẽ làm bạn đau đầu khi check lỗi đó. (hyutars <-- kinh nghiệm đau thương T_T). Lồng các câu lệnh if vào nhau: Câu lệnh if có thể làm chương trình thực thi câu lệnh hay một khối lệnh, trong đó gồm các câu lệnh if khác. Khi bạn viết một câu lệnh if bên trong câu lệnh if khác, nó gọi là “Lồng vào nhau”(nesting). Trong đoạn code tiếp theo, câu lệnh if bắt đầu với if (score >= 1000) được lồng bên trong câu lệnh if bắt đầu với (score > 500). Mã: if (score > 500) { cout << "You scored at least 500.\n"; if (score >= 1000) cout << "You scored 1000 or more!\n"; } Vì score lớn hơn 500, chương trình đi vào thực hiện khối lệnh bên trong câu lệnh if đầu tiên, và nó hiện thị lời bình luận You scored at least 500. Sau đó, ở câu lệnh if bên trong, chương trình so sánh score với 1000. Vì score “lớn hơn hoặc bằng” với 1000 (trường hợp này nó rơi vào “bằng”), chương trình hiển thị lời bình luận, You scored 1000 or more! Sau đó chương trình tiếp tục các câu lệnh phía sau khối lệnh. Lưu ý: Bạn có thể lồng vào bao nhiều câu lệnh if tùy ý. Nhưng, nếu bạn lồng quá nhiều, code sẽ rất khó đọc. Tóm lại, bạn nên lồng các câu lệnh vào nhau ở một mức độ vừa phải.
Cám ơn đã động viên ::) Chiều nay mới thi xong môn cuối cùng, từ nay sẽ up thường xuyên cho anh em hơn ^^. Bài 2.3: Dùng mệnh đề else Bạn có thể thêm mệnh đề else vào sau câu lệnh if, chức năng của else là cho câu lệnh (hay khối lệnh) trong nó được thực hiện nếu biểu thức kiểm tra trong if là false. Sau đây là dạng tổng quát: Mã: if (biểu thức) câu lệnh 1; // hoặc khối lệnh else câu lệnh 2; // hoặc khối lệnh Nếu biểu thức là true, câu lệnh 1 sẽ được thực hiện. Sau đó chương trình sẽ bỏ qua câu lệnh 2 và thực hiện các câu lệnh sau khối if. Nếu biểu thức là false, câu lệnh 1 sẽ bị bỏ qua và câu lệnh 2 sẽ được thi hành. Sau khi câu lệnh 2 được thực thi xong, chương trình sẽ xử lý các câu lệnh sau khối if. Giới thiệu chương trình Score Rater 2.0: Chương trình Score Rater cũng làm chức năng bình chọn điểm, do người dùng nhập vào. Nhưng lần này, chương trình dùng câu lệnh if kết hợp với else. Hình 2.2 và 2.3 cho thấy hai thông điệp khác nhau được chương trình cho hiển thị tùy vào số điểm mà người dùng nhập vào. Hình 2.2 – Nếu người dùng nhập vào điểm số nhiều hơn 500, người đó sẽ được chúc mừng. Hình 2.3 – Nếu người dùng nhập vào số điểm nhỏ hơn hoặc bằng 500, người đó sẽ được thông báo: chẳng có gì đáng để tự hào cả. Mã: // Score Rater 2.0 // Demonstrates the else clause #include <iostream> using namespace std; int main() { int score; cout << "Enter your score:"; cin >> score; if (score > 500) cout << "\nYou got over 500. Nice score.\n"; else cout << "\nYou got 500 or less. Nothing to brag about.\n"; return 0; } Tạo hai đường rẽ nhánh: Bạn đã thấy phần đầu tiên của câu lệnh if, và nó hoạt động cũng giống như trước thôi. Nếu score (điểm số) lớn hơn 500, thông điệp You got over 500. Nice score, được hiển thị. Mã: if (score > 500) cout << "\nYou got over 500. Nice score.\n"; Và đây là cái mới. Mệnh đề else cho phép chương trình rẽ nhánh vào nếu điều kiện của câu lệnh if là false. Cho nên if (score > 500) là false, chương trình bỏ qua thông điệp đầu tiên và thay vào đó, thông điệp You got 500 or less. Nothing to brag about. được hiển thị. Mã: else cout << "\nYou got 500 or less. Nothing to brag about.\n"; Kết hợp mệnh đề else cho đúng với câu lệnh if: Cẩn thận, vì else sẽ kết hợp với câu lệnh if ở gần nó nhất. Nếu bạn không cẩn thận, nó có thể dẫn đến lỗi logic và kết quả không như ý. Sau đây là đoạn code ví dụ (không thuộc chương trình Score Rater) : Mã: if (false) if (true) cout << "This will never be displayed."; else cout << "This will always be displayed."; Nếu bạn nhìn thoáng qua, bạn có thể tưởng thông điệp This will always be displayed sẽ luôn được hiển thị. Nhưng thực tế, chuyện đó sẽ không bao giờ xảy ra vì else không kết hợp với if (false), nó kết hơp với if (true). Bằng cách thêm một khoảng trắng, bạn có thể thấy rõ đoạn code này hoạt động thế nào: Mã: if (false) if (true) cout << "This will never be displayed."; else cout << "This will always be displayed."; Tôi không hề thay đổi ý nghĩa của đoạn code (nên nhớ, khoảng trắng chỉ có ý nghĩa với con người), tôi chỉ làm cho đoạn code rõ ràng hơn. Vì điều kiện câu lệnh if bên ngoài là false, khối lệnh bên trong nó không bao giờ được thi hành, không có thông điệp nào được hiển thị cả. Tôi có thể giải quyết vấn đề này bằng cách thêm hai dấu ngoặc móc. Mã: if (false) { if (true) cout << "This will never be displayed."; } else cout << "This will always be displayed."; Kết quả, câu lệnh if thứ hai được đặt vào một thế giới nhỏ hơn (gọi là phạm vi - scope) bên trong khối lệnh, và mệnh đề else kết hợp với if (false). Bây giờ đoạn code sẽ luôn hiển thị thông điệp This will always be displayed. Lưu ý: Khái niêm scope khá quan trọng, nhưng tôi sẽ dành nó cho Chương 5. Bây giờ, các bạn cứ nghĩ scope là một thế giới nhỏ hơn.
Bài 2.4 : Dùng câu lệnh switch: Bạn có thể dùng câu lệnh switch để tạo nên nhiều điểm rẽ nhánh trong đoạn code. Sau đây là cú pháp: Mã: switch (choice) { case value1: statement1; break; case value2: statement2; break; case value3: statement3; break; . . . case valueN: statementN; break; default: statementN + 1; } hyutars: choice: lựa chọn, value: giá trị, statement: câu lệnh (hoặc khối lệnh, lưu ý phải có dấu {}), default: mặc nhiên, mặc định. Câu lệnh kiểm tra lần lượt giá trị của choice với value1, value2, và value3. Nếu choice tương đương với giá trị nào, chương trình sẽ thực hiện câu lệnh (khối lệnh) tương ứng. Khi chương trình gặp câu lệnh break, nó sẽ thoát khỏi cấu trúc switch. Nếu choice không tương đương value nào cả thì câu lệnh ở chỗ default sẽ được thực hiện. Dùng break và default là tùy ý, không bắt buộc. Nếu bạn không dùng break, chương trình sẽ tiếp tục thực hiện các câu lệnh tiếp theo cho đến khi nó gặp break hoặc default hoặc cho đến khi câu lệnh switch kết thúc. Thường thì bạn dùng một câu lệnh break cho mỗi case. Và mặc dù default là tùy ý, nhưng bạn nên dùng nó, có nó thì chương trình của bạn sẽ bao quát được tất cả các trường hợp. Sau đây là ví dụ. Giả sử choice có giá trị bằng value2. Đầu tiên chương trình sẽ thử so sánh choice với value1. Vì chúng không bằng nhau, chương trình sẽ tiếp tục. Tiếp theo, chương trình sẽ so sánh choice với value2. Vì chúng bằng nhau nên chương trình sẽ thực hiện statement2. Sau đó chương trình gặp câu lệnh break và thoát khỏi khối switch. Bẫy: Bạn chỉ được dùng câu lệnh switch để kiểm tra giá trị thuộc kiểu int (hoặc giá trị có thể xem như là int, như char hay enumerator). Câu lệnh switch không hoạt động với các kiểu dữ liệu khác. Giới thiệu chương trình Menu Chooser: Chương trình Menu Chooser tái hiện lại một menu các mức độ khó của trò chơi và yêu cầu người chơi chọn. Nếu người chơi nhập vào số tương ứng với các lựa chọn (choice) trong menu, anh ta sẽ nhận được thông điệp xác nhận lại lựa chọn. Nếu người chơi nhập vào số khác, anh ta sẽ được thông báo rằng đã nhập sai. Hình 2.4 cho thấy chương trình đang hoạt động: Hình 2.4 – Người chơi chọn mức độ Easy. Mã: // Menu Chooser // Demonstrates the switch statement #include <iostream> using namespace std; int main() { cout << "Difficulty Levels\n\n"; cout << "1 - Easy\n"; cout << "2 - Normal\n"; cout << "3 - Hard\n\n"; int choice; cout << "Choice:"; cin >> choice; switch (choice) { case 1: cout << "You picked Easy.\n"; break; case 2: cout << "You picked Normal.\n"; break; case 3: cout << "You picked Hard.\n"; break; default: cout << "You made an illegal choice.\n"; } return 0; } Tạo nên nhiều đường rẽ nhánh: Câu lệnh switch tạo nên bốn điểm rẽ nhánh. Nếu người chơi nhập vào số 1, đoạn code ở case 1 sẽ được thực hiện và You picked Easy sẽ được hiển thị. Nếu người chơi nhập vào số 2, đoạn code ở case 2 được thực hiện và You picked Normal được hiển thị. Nếu nhập vào số 3, đoạn code ở case 3 được thực hiện và You picked Hard được hiển thị. Nếu người chơi nhập vào các giá trị khác, đoạn code ở default được thực hiện và thông điệp You made an illegal choice được hiển thị. Bẫy: Thường thì hầu như kết thúc mỗi case đều dùng câu lệnh break. Đừng quên nó, nếu không, chương trình của bạn có thể làm những điều mà bạn không mong muốn.
Bài 2.5: Dùng vòng lặp while Vòng lặp while cho phép bạn lặp đi lặp lại một đoạn code trong khi biểu thức kiểm tra là true. Sau đây là cú pháp: Mã: while (expression) statement; // hoặc khối lệnh Nếu expression là false, chương trình đi đến các câu lệnh phía sau vòng lặp. Nếu expression là true, chương trình thực thi statement, sau đó vòng lặp tiếp tục kiểm tra expression. Vòng lặp cứ thực thi cho đến khi nào expression là false, lúc đó vòng lặp kết thúc. Giới thiệu chương trình Play Again: Chương trình Play Again tái hiện lại một trò chơi hấp dẫn (ta rút gọn quá trình chơi lại thành **Played an exciting game**). Sau đó (tức là chơi xong), chương trình hỏi người chơi có muốn chơi tiếp hay không. Nếu anh ta nhập y, anh ta sẽ được chơi lại (nên nhớ ta chỉ mô phỏng quá trình chơi thành hiển thị thông điệp **Played an exciting game**). Chương trình dùng vòng lặp while để giải quyết bài toán này. Hình 2.5 cho thấy chương trình đang hoạt động. Hình 2.5 – Tạo nên một vòng lặp nhờ while. Mã: // Play Again // Demonstrates while loops #include <iostream> using namespace std; int main() { char again = ‘y'; while (again == ‘y') { cout << "\n**Played an exciting game**"; cout << "\nDo you want to play again? (y/n):"; cin >> again; } cout << "\nOkay, bye."; return 0; } Tạo vòng lặp với while: Việc đầu tiên chương trình làm trong hàm main() là khai báo một biến kiểu char tên là again, và khởi tạo nó là ‘y’. Sau đó chương trình bắt đầu vòng lặp while bằng cách kiểm tra again có bằng ‘y’ hay không. Vì nó là true, chương trình hiển thị thông điệp **Played an exciting game**, sau đó hỏi người chơi có muốn chơi tiếp hay không, và lưu giữ câu trả lời ở biến again. Vòng lặp tiếp tục nếu người chơi nhập vào y, ngược lại sẽ thoát khỏi vòng lặp. Bạn hãy chú ý, tôi phải khởi tạo biến again ở trước vòng lặp vì nó được dùng trong biểu thức kiểm tra của vòng lặp. Vì vòng lặp while kiểm tra biểu thức của nó trước khi đi vào thân vòng lặp (loop body - tức khối lệnh được lặp lại), cho nên bạn phải chắc rằng bất cứ biến nào tham gia biểu thức kiểm tra phải có giá trị trước khi vòng lặp bắt đầu (hyutars: ví dụ trong bài này nếu again không được khởi tạo là ‘y’ thì lúc đầu làm sao nó thực hiện phép so sánh (again == ‘y’) được, đúng không nào ^^).
Bài 2.6: Dùng vòng lặp do Giống như vòng lặp while, vòng lặp do cho phép bạn lặp lại một đoạn code dựa theo điều kiện nào đó. Điểm khác biệt của vòng lặp do là nó kiểm tra điều kiện sau khi thực hiện mỗi vòng lặp (while là kiểm tra điều kiện trước mỗi vòng lặp). Có nghĩa là, thân vòng lặp luôn được thực hiện ít nhất một lần. Sau đây là cú pháp: Mã: do statement; while (expression); Chương trình thi hành statement trước, sau đó mới kiểm tra expression, nếu nó true, vòng lặp sẽ tiếp tục. Ngược lại, nếu expression trả về giá trị false thì vòng lặp sẽ kết thúc. Giới thiệu chương trình Play Again 2.0: Chương trình Play Again 2.0 giống y như chương trình Play Again ở bài trước. Chỉ có điều chương trình dùng vòng lặp do thay cho vòng lặp while. Hình 2.6 cho thấy chương trình đang hoạt động: Mã: // Play Again 2.0 // Demonstrates do loops #include <iostream> using namespace std; int main() { char again; do { cout << "\n**Played an exciting game**"; cout << "\nDo you want to play again? (y/n):"; cin >> again; } while (again == 'y'); cout << "\nOkay, bye."; return 0; } Tạo vòng lặp với do: Trước khi vòng lặp do bắt đầu, tôi khai báo biến again thuộc kiểu char. Tôi không cần phải khởi tạo nó vì nó chưa được kiểm tra cho đến khi chương trình thực hiện xong vòng lặp thứ nhất. Tôi nhận giá trị mới cho biến again từ người chơi ở thân vòng lặp. Sau đó tôi kiểm tra expression. Nếu nó bằng ‘y’, vòng lặp được lặp lại, ngược lại, vòng lặp kết thúc. Trong thực tế: Mặc dù bạn có thể dùng vòng lặp while và do để thay thế lẫn nhau, nhưng đa số lập trình viên thường dùng vòng lặp while. Có thể là vòng lặp do nhìn tự nhiên hơn trong một số trường hợp, nhưng lợi thế của vòng lặp while là expression xuất hiện ngay phía trên vòng lặp, bạn không cần phải tìm kiếm đến dòng cuối của vòng lặp để thấy nó. Bẫy: Nếu bạn đã từng mắc kẹt khi chơi một game nào đó bởi chu trình không bao giờ kết thúc, bạn đã có kinh nghiệm về vòng lặp vô hạn (infinite loop) – vòng lặp không bao giờ kết thúc. Sau đây là một ví dụ đơn giản của vòng lặp vô hạn: Mã: int test = 10; while (test == 10) cout << test; Trong trường hợp này, vòng lặp được thực hiện vì test là 10. Nhưng vì test không bao giờ thay đổi, vòng lặp sẽ không bao giờ kết thúc. Kết quả, người dùng phải “kill” chương trình đang chạy để kết thúc nó. Bài học? Hãy luôn chắc rằng expression của vòng lặp đến một lúc nào đó phải trả về giá trị false. (hyutars: Có một số kĩ thuật lập trình rất hay dùng vòng lặp vô hạn, chẳng hạn như để tạo menu ... nếu bạn muốn xài vòng lặp vô hạn thì nhớ dùng break hoặc exit(1) để kết thúc nó, bạn không cần vội, cứ học đến các bài sau thì bạn sẽ biết ^^).
Dùng câu lệnh break và continue: Chúng ta có thể dung 2 câu lệnh này để thay đổi quy luật bình thường của vòng lặp. Bạn có thể thoát khỏi vòng lặp với câu lệnh break, và bạn có thể nhảy đến phần đầu vòng lặp với câu lệnh continue. Nó rất hữu dụng trong một số trường hợp. Giới thiệu chương trình Finicky Counter: Chương trình Finicky Counter đếm từ 1 đến 10 bằng cách sử dụng vòng lặp while. Nó finicky (khó tính) vì nó không thích số 5 – nó sẽ bỏ qua con số này. Hình 2.7 cho thấy chương trình đang hoạt động: Hình 2.7 – Số 5 được bỏ qua với câu lệnh continue, và vòng lặp kết thúc với câu lệnh break. Mã: // Finicky Counter // Demonstrates break and continue statements #include <iostream> using namespace std; int main() { int count = 0; while (true) { count += 1; //end loop if count is greater than 10 if (count > 10) break; //skip the number 5 if (count == 5) continue; cout << count << endl; } return 0; } Tạo vòng lặp while(true): Tôi tạo vòng lặp với dòng này: Mã: while (true) Kĩ thuật này dùng để tạo nên một vòng lặp vô hạn. Nó có vẻ kì cục đúng không? Vì tôi đã cảnh báo là coi chừng rơi vào vòng lặp vô hạn. Nhưng thực ra đây không phải là vòng lặp vô hạn thực sự vì tôi để điều kiện thoát cho nó ở thân vòng lặp. Lưu ý: Mặc dù while (true) nhìn có vẻ rõ ràng hơn vòng lặp truyền thống, nhưng bạn cũng nên cố gắng hạn chế dùng vòng lặp này. Nếu chương trình có sử dụng vòng lặp while (true), nó thường là vòng lặp chính bao gồm một số lượng lớn code. Dùng câu lệnh break để thoát khỏi vòng lặp: Sau đây là điều kiện thoát khỏi mà tôi đặt vào vòng lặp: Mã: //end loop if count is greater than 10 if (count > 10) break; Vì count được tăng lên 1 mỗi lần thân vòng lặp bắt đầu, cho nên nó sẽ đạt giá trị 11. Khi đó, câu lệnh break (có nghĩa là thoát khỏi vòng lặp) được thực thi và vòng lặp kết thúc. Dùng câu lệnh continue để nhảy về phần đầu vòng lặp: Trước khi biến count được xuất ra màn hình, tôi đặt đoạn code này: Mã: //skip the number 5 if (count == 5) continue; Câu lệnh continue có nghĩa là “nhày về phần đầu vòng lặp”. Ở phần đầu vòng lặp (while (true)), điều kiện của vòng lặp while được kiểm tra và vòng lặp được thực thi lần nữa (vì nó là true). Cho nên khi count bằng 5, chương trình không đến được dòng cout << count << endl;. Thay vào đó, nó quay trở lại phần đầu của vòng lặp. Như kết quả, 5 được bỏ qua và không bao giờ được hiển thị. Hiểu khi nào dùng break và continue: Bạn có thể dùng break và continue trong mọi vòng lặp mà bạn tạo ra, chúng không chỉ dành cho vòng lặp while. Nhưng bạn nên dùng chúng ở một mức vừa phải. Nếu không sẽ rất khó xác định đường đi của vòng lặp.
Cám ơn bạn, mình sẽ cố gắng ^^ Bài 2.8: Dùng toán tử logic: Cho đến lúc này bạn đã khá nhuần nhuyễn khi xác định giá trị true hay false của các biểu thức (expression). Bạn có thể kết hợp các biểu thức với nhau bởi toán tử logic để tạo thành một biểu thức phức tạp hơn. Bảng 2.2 cho thấy danh sách các toán tử: Giới thiệu chương trình Designers Network: Chương trình Designers Network tài hiện lại một mạng máy tính, trong đó chỉ có những thành viên của nhóm game designers mới được truy cập. Giống như trong hệ thống máy tính ngoài đời vậy, mỗi member phải nhập tên (Username) và mật mã (Password) để login. Nếu login thành công, member đó sẽ được chào mừng. Guest (khách) cũng có thể login, nhưng quyền (hyutars: trong bản gốc là security level, nhưng mình thấy để từ này thích hợp hơn) của họ thấp hơn member. Để login với tư cách là guest, người dùng phải nhập vào từ “guest” ở ô username hoặc pasword. Hình 2.8 cho đến hình 2.10 cho thấy chương trình đang hoạt động: Hình 2.8: Nếu bạn không phải là member hoặc guest, bạn không thể login. Hình 2.9: Nếu bạn login với tư cách là guest, quyền (biến security) của bạn sẽ được set ở mức thấp. Hình 2.10: Có vẻ như một người thuộc nhóm member login. Mã: // Designers Network // Demonstrates logical operators #include <iostream> #include <string> using namespace std; int main() { cout << "\tGame Designer's Network\n"; int security = 0; string username; cout << "\nUsername:"; cin >> username; string password; cout << "Password:"; cin >> password; if (username == "S.Meier" && password == "civilization") { cout << "\nHey, Sid."; security = 5; } if (username == "S.Miyamoto" && password == "mariobros") { cout << "\nWhat's up, Shigeru?"; security = 5; } if (username == "W.Wright" && password == "thesims") { cout << "\nHow goes it, Will?"; security = 5; } if (username == "guest" || password == "guest") { cout << "\nWelcome, guest."; security = 1; } if (!security) cout << "\nYour login failed."; return 0; } Dùng toán tử logic AND: Toán tử logic AND, &&, cho phép bạn kết hợp hai expression (biểu thức) thành một cái lớn hơn, expression lớn hơn này cũng trả về giá trị true và false. Expression lớn là true khi và chỉ khi cả hai expression nhỏ là true; ngược lại, nó là false. Trong tiếng Anh, “and” có nghĩa là cả hai. Cả hai expression nhỏ phải là true để expression lớn là true. Sau đây là ví dụ cụ thể từ chương trình Designers Network: Mã: if (username == "S.Meier" && password == "civilization") Expression: username == "S.Meier" && password == "civilization" là true khi và chỉ khi cả hai expression username == "S.Meier" và password == "civilization" là true. Đoạn code này hoạt động rất tốt vì tôi chỉ muốn cho Sid được truy cập nếu anh ta nhập đúng cả username và password. Những người khác sẽ không vào được tài khoản của anh ta. Một cách khác để hiểu toàn tử && hoạt động như thế nào, đó là nhìn vào bảng 2.3 sau đây: Tất nhiên, chương trình Designers Network cũng cho phép các user khác ngoài Sid Meier. Thông qua một dãy các câu lệnh if và toán tử &&, chương trình kiểm tra tiếp 3 cặp username và password nữa. Nếu người dùng nhập đúng, anh ta sẽ được chào mừng và được cấp phát security level (hyutars: dịch là “quyền” cho dễ hiểu nhé). Dùng toán tử logic OR: Toán tử logic OR, ||, cho phép bạn kết hợp hai expression (biểu thức) thành một cái lớn hơn, expression lớn hơn này cũng trả về giá trị true và false. Expression lớn là true khi có ít nhất một trong hai expression nhỏ là true; ngược lại (tức là cả hai expression nhỏ đều false), nó là false. Trong tiếng Anh, “or” có nghĩa là “hoặc”. Nếu có ít nhất một trong hai expression là true thì expression lớn sẽ là true. Sau đây là ví dụ từ chương trình Designers Network: Mã: if (username == "guest" || password == "guest") Expression: username == "guest" || password == "guest" là true nếu username == "guest" là true hoặc password == "guest" là true (nếu cả hai cái true luôn thì càng tốt). Nếu người dùng nhập vào từ guest ở ô username hay ở ô password hoặc cả hai thì người dùng được login vào với tư cách là guest. Sau đây là bảng 2.4 để minh họa toàn tử OR ( || ): Dùng toán tử logic NOT: Toán tử logic NOT, ! (dấu cảm than), cho phép bạn hoán đổi giữa giá trị true và false của một biểu thức. Nếu expression ban đầu là true, expression mới sẽ là false; nếu expression ban đầu là false, expression mới sẽ là true. Giống như trong tiếng Anh, “not” có nghĩa là ngược lại. Expression mới có giá trị ngược lại với expression ban đầu. Sau đây là ví dụ: Mã: if (!security) Expression: !security là true khi security là false (hoặc 0). Trong chương trình, biến security là 0 khi và chỉ khi việc login bị thất bại, trong trường hợp này, tôi sẽ thông báo cho người dùng biết việc login đã thất bại: Your login failed. Expression !security là false khi security là true hoặc là một giá trị khác 0. Nếu security khác 0, người dùng đăng nhập thành công, thông điệp Your login failed sẽ không xuất hiện. Nhìn vào bảng 2.5 để hiểu hơn về toán từ !: Độ ưu tiên của các toán tử: Giống như các toán tử số học, toán tử logic cũng có độ ưu tiên quy định cái nào được thực hiện trước và cái nào được thực hiện sau. Toán tử logic NOT, !, có độ ưu tiên cao hơn toán tử AND, &&, và toán tử này lại có độ ưu tiên cao hơn OR, || (! > && > ||). Giống như toán tử số học, nếu bạn muốn toán tử có độ ưu tiên thấp hơn được thực hiện trước thì hãy để nó trong dấu ngoặc đơn. Bạn có thể tạo ra biểu thức (expression) phức tạp bao gồm toán tử số học, toán tử quan hệ và toán tử logic. Độ ưu tiên của toán tử quyết định thứ tự thực hiện. Như thế nào đi nữa, tốt nhất là nên tạo ra các expression đơn giản dễ nhìn, sẽ không có ai phải nhìn vào bảng thứ tự ưu tiên để đọc trật tự expression của bạn cả. Để xem bảng độ ưu tiên của các toán tử các bạn có thể lục lại ở mấy bài trước, mình đã post rồi. Mẹo: Nếu bạn dùng dấu ngoặc đơn trong một expression lớn để thay đổi trật tự thực hiện, bạn nên dùng redundant parentheses (dấu ngoặc đơn thừa) – dấu này không làm thay đổi giá trị của expression – nó làm cho expression rõ ràng hơn. Ví dụ: Mã: (username == "S.Meier" && password == "civilization") Bây giờ hãy thử dùng dấu ngoặc đơn thừa: Mã: ((username == "S.Meier") && (password == "civilization")) Dấu ngoặc đơn thừa không làm thay đổi giá trị của expression lớn, nhưng nó giúp tách biệt hai expression nhỏ hơn. Đoạn code trông có vẻ rõ ràng hơn đúng không? Dùng dấu ngoặc đơn thừa là một thủ thuật nhỏ trong các thủ thuật lập trình, sử dụng các thủ thuật lập trình? đó là công việc mà bạn – một lập trình viên giỏi – phải làm.
Bài 2.9: Tạo ra một số random (ngẫu nhiên) Những tình huống không thể dự đoán trước được sẽ làm nên sự hấp dẫn cho game. Chẳng hạn, sự thay đổi chiến thuật bất ngờ của đối thủ trong game dàn trận, hoặc sự xuất hiện bất ngờ của một con quái vật trong action game. Tạo nên một con số ngẫu nhiên là một kĩ thuật cơ bản của dạng này. Giới thiệu chương trình Die Roller: (đổ xúc xắc) Chương trình Die Roller tái hiện lại việc đổ một con xúc xắc có sáu mặt. Máy tính sẽ tính toán mặt nào sẽ xuất hiện một cách ngẫu nhiên. Hình 2.11 cho thấy kết quả của chương trình: Hình 2.11: Chương trình tạo ra một con số ngẫu nhiên. Gọi hàm rand(): Một trong những điều tôi làm đầu tiên là include file mới: Mã: #include <cstdlib> File cstdlib chứa những hàm tương tác với con số ngẫu nhiên. Vì tôi đã include nó, tôi được phép gọi hàm thuộc file này, cụ thể là hàm rand(), đó là những gì tôi làm ở hàm main(): Mã: int randomNumber = rand(); //tạo một con số ngẫu nhiên Như bạn đã học ở Chương 1, hàm là một đoạn code có thể làm việc gì đó, sau đó trả lại một giá trị. Bạn gọi hàm bằng cách sử dụng tên nó, sau đó là hai dấu ngoặc đơn, (hyutars: bên trong hai dấu ngoặc đơn có thể có các đổi). Nếu hàm trả về một giá trị, bạn có thể gán giá trị đó cho một biến. Tôi sử dụng một câu lệnh gán, gán giá trị trả về của hàm rand() (một con số ngẫu nhiên) cho biến randomNumber. Lưu ý: Hàm rand() tạo ra một con số ngẫu nhiên từ 0 đến ít nhất là 32767. Tôi không thể nói trước giá trị lớn nhất do hàm rand() tạo ra vì nó phụ thuộc vào sự bổ sung của bạn trong C++ (nguyên văn: depends on your implementation of C++). Giá trị lớn nhất được lưu giữ trong hằng số RAND_MAX, được định nghĩa trong file cstdlib. Cho nên nếu bạn muốn biết giá trị lớn nhất hàm rand() có thể tạo ra, chỉ cần cout RAND_MAX. Hàm còn có thể lấy các giá trị tử bên ngoài vào để sử dụng. Bạn cung cấp các giá trị đó cho hàm bằng cách đặt chúng ở giữa hai dấu ngoặc đơn, tách biệt các giá trị đó bởi dấu phẩy. Những giá trị đó được gọi là đối số, và khi đó, bạn đã trao quyền sử dụng chúng cho hàm. Tôi không để giá trị nào ở hàm rand() vì hàm đó không cần sử dụng đối số nào cả. Nhân tố tạo ra số ngẫu nhiên: (Seeding the Random Number Generator) Hàm rand() tạo ra một số giả ngẫu nhiên (không phải là số ngẫu nhiên thật) dựa vào một công thức. Để hiểu vấn đề này ta cứ tưởng tượng rand() đọc một cuốn sách lớn mà nội dung của nó là các số ngẫu nhiên. Nhưng rand() luôn luôn bắt đầu tại trang đầu của cuốn sách lúc chương trình chạy. Điều đó có nghĩa là rand() luôn cho ra những số giống nhau mỗi khi chương trình chạy. (hyutars: bạn có thể kiểm chứng điều này bằng cách bỏ hàm srand đi) Bạn có thể sửa lại vị trí bắt đầu của rand() trong cuốn sách bằng cách dùng hàm srand() (cũng được định nghĩa trong cstdlib). Số ở giữa hai dấu ngoặc đơn hàm srand() là nhân tố để tạo ra số ngẫu nhiên, và để chúng ta có những số ngẫu nhiên thực sự, nhân tố đó phải là một con số khác nhau mỗi khi chương trình chạy. Cách tốt nhất là sử dụng hàm time() (được định nghĩa trong ctime), nó sẽ trả về một con số dựa vào thời gian hiện hành. Để có thể sử dụng hàm time(), tôi include file ctime ở đầu chương trình: Mã: #include <ctime> Sau đó, trong hàm main(), tôi tạo ra số ngẫu nhiên dựa vào thời gian hiện tại: Mã: srand(time(0)); // seed random number generator based on current time Trong đoạn code trên, tôi dùng hàm time(0) như là một đối số của hàm srand(). Nghĩa là giá trị trả về của hàm time(0) sẽ được sử dụng bởi hàm srand(). Tổng quát, bạn có thể dùng hàm này để làm đối số cho hàm kia, miễn sao giá trị trả về của hàm làm đối số phải hợp lệ. Trong hàm time(0), tôi đã đặt đối số là 0 vào. time(0) sẽ trả về một giá trị dựa vào thời gian hiện hành, sau đó tôi đưa giá trị đó cho hàm srand() -> tạo ra một số thực sự ngẫu nhiên. Thu hẹp vùng tạo ra số ngẫu nhiên: Sau khi tạo ra một số ngẫu nhiên, biến randomNumber giữ một giá trị từ 0 đến 32 767. Nhưng tôi muốn số đó phải từ 1 đến 6 thôi(đang chơi trò đổ xúc xắc mà), do đó tôi dùng câu lệnh này để thu hẹp phạm vi đó lại: Mã: int die = (randomNumber % 6) + 1; // get a number between 1 and 6 Tất cả những số dương khi chia cho 6 đều có số dư từ 0 đến 5. Trong câu lệnh trên, tôi lấy số dư và cộng thêm 1, phạm vi của biến die bây giờ sẽ là 1 đến 6 – đúng như điều chúng ta mong muốn. Bạn có thể dùng kĩ thuật này để điều chỉnh phạm vi xuất hiện của số ngẫu nhiên. Bẫy: Dùng toán tử modulus (chia lấy dư) để điều chỉnh phạm vi xuất hiện của một số ngẫu nhiên không phải lúc nào cũng đúng tuyệt đối. Sẽ có những con số xuất hiện nhiều hơn so với các con số khác. Nhưng dù thế nào đi nữa, đây không phải là vấn đề đối với một game đơn giản.
Có thể nói là rất lâu >_<, do thời gian của mình cũng không cố định, các bạn chờ mình dịch xong biên tập thành e-books ròi đọc >_< Bài 2.10: Hiểu được vòng lặp trong game Vòng lặp trong game (game loop) là một hình thức chung tái hiện lại các sự kiện trong game. Lõi của các sự kiện lặp đi lặp lại, đó là lí do tại sao ta gọi nó là vòng lặp. Mặc dù hình thức của mỗi game có khác nhau, nhưng cấu trúc bên trong của chúng đều giống nhau. Dù bạn đang nói về một game bắn nhau đơn giản hay một game RPG phức tạp, bạn đều có thể thoát khỏi game để đi đến một phần nào đó của vòng lặp. Hình 2.12 cho thấy một ví dụ trực quan về vòng lặp trong game: Hình 2.12: Vòng lặp game này đúng với tất cả các game. Sau đây là giải thích: Setting up: Cài đặt cấu hình cho game như sound, music và graphics. Người chơi có thể còn được cho xem những lời giới thiệu về cốt truyện của game và nhiệm vụ của anh ta. Getting player input: Dù người chơi nhập vào cái gì, từ bàn phím, con chuột, cần điều khiển …, nó đều được lưu giữ lại để xử lý. Updating game internals: Luật chơi của game sẽ được thi hành dựa vào player input. Ví dụ, nó sẽ cho phép nhân vật dùng item, hoặc dùng một magic nào đó. Updating the display: Trong đại đa số các game, quá trình này khiến phần cứng máy tình phải hoạt động nhiều vì nó thường liên quan đến việc vẽ đồ họa. Nhưng nó cũng có thể đơn giản như hiển thị một dòng text. Checking whether the game over: Nếu game chưa kết thúc ( chẳng hạn nếu nhân vật vẫn còn sống và người chơi chưa muốn thoát khỏi game), chương trình lại đi đến phần Getting player input. Nếu game kết thúc, chương trình đi đến phần Shutting down. Shutting down: Tại điểm này, game kết thúc. Người chơi thường được nhận những thông tin cuối cùng, chẳng hạn như điểm số của anh ta. Chương trình giải phóng các nguồn tài nguyên nếu cần thiết, sau đó exit. hyutars: Bài này có mục đích định hướng tí xíu cho những ai muốn lập trình game, nếu không thì không cần chú ý đến bài này lắm.