111
Bài 47: Trình bày phân số
| Trình bày phân số [Hoàng Ngọc Giao] |
|
Trong trò chơi "tìm số nhỏ nhất", ta đã dùng đến phân số, chẳng hạn 45/100, và cả hỗn số, chẳng hạn 3 3/4. Để hiển thị phân số và hỗn số dưới dạng quen thuộc với học sinh tiểu học, ta cần có một thanh ngang ở giữa ô. Khi đó, tử số được đặt trên thanh ngang, mẫu số được đặt dưới thanh ngang và phần nguyên của hỗn số được đặt trước thanh ngang. Muốn vậy, trong nhân vật Tile, bạn cần vẽ thêm một thanh ngang, tạo thêm hai dòng chữ động ở trên và dưới thanh ngang. Sau đó, trong chương trình, ta tìm cách phân biệt phân số, hỗn số với số thông thường và chọn cách hiển thị thích hợp cho từng trường hợp. |
| Bạn hãy mở lại trò chơi trong Flash, gõ phím F11 để mở bảng Library, bấm kép vào biểu tượng ở đầu dòng Tile (trong danh sách nhân vật) để bắt đầu chỉnh sửa nhân vật Tile. |
| Bạn bấm chọn dòng chữ động hiện có ở giữa ô, ấn Ctrl + C để sao chép, ấn Ctrl + V để làm xuất hiện bản sao, rồi dùng các phím mũi tên để di chuyển bản sao đó đến vị trí phía trên dòng chữ động hiện có (hình 1). Bạn mở bảng Properties, bấm vào ô Instance Name và gõ tên label2, nhằm phân biệt với dòng chữ động đầu tiên mang tên label. Ta sẽ dùng label2 để hiển thị tử số của phân số. |
![]() |
| Bạn lại ấn Ctrl + V để làm xuất hiện bản sao thứ hai và cũng dùng các phím mũi tên để di chuyển bản sao đến vị trí phía dưới dòng chữ động đầu tiên (hình 2). Bấm vào ô Instance Name, bạn gõ label3 để đặt tên cho dòng chữ động thứ ba trong ô. Ta sẽ dùng dòng chữ động label3 để hiển thị mẫu số. |
![]() |
| Trước khi vẽ thêm thanh ngang, bạn cần tạo lớp mới để thao tác thuận tiện. Bạn bấm nút Insert Layer ở bảng Timeline. Lớp Layer 2 xuất hiện. bạn bấm vào biểu tượng ổ khóa ở dòng Layer 1 để khóa lớp Layer 1 và bấm vào dòng Layer 2. Điều này giúp bạn vẽ dễ dàng trên lớp Layer 2, không làm "xộc xệch" những gì đã có trong lớp Layer 1. |
| Bạn chọn công cụ vẽ hình khung |
![]() |
| Bạn gõ phím F8 để mở hộp thoại Convert to Symbol, gõ tên Bar và bấm OK. Thao tác như vậy chuyển thanh ngang vừa vẽ thành nhân vật mang tên Bar. Bạn mở bảng Properties, bấm vào ô Instance Name, gõ bar để đặt tên cho thể hiện của nhân vật Bar bên trong nhân vật Tile. |
| Việc chỉnh sửa nhân vật Tile đã xong, bạn bấm vào Scene 1 |
|
... function displayNumbers() { var num, str; var k; for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { ... str = getString(num); tiles[i][j].num = num; //tiles[i][j].label.text = str; setLabel(tiles[i][j], str); } } }
function setLabel(t, str) { t.label.text = str; } ... |
| Thay cho câu lệnh quy định nội dung của label trong từng ô, ta gọi hàm setLabel. Hàm setLabel có hai đối mục: đối mục đầu tiên là một ô nào đó trên sân khấu, đối mục thứ hai là nội dung cần được đưa vào ô đó. Chạy thử chương trình, bạn không thấy có gì mới, chỉ biết chắc rằng việc gọi hàm setLabel suôn sẻ, không có gì trục trặc, chuỗi str được trao cho hàm setLabel được đưa vào dòng chữ động label trong ô đang xét một cách êm thắm. Tuy nhiên, bạn thấy thanh ngang xuất hiện trong từng ô một cách... vô duyên. Ta hãy làm cho hàm setLabel "thông minh" hơn, biết đọc chuỗi được trao và phân biệt trường hợp phân số với số thông thường, chỉ dùng thanh ngang trong trường hợp chuỗi được trao biểu diễn một phân số. Bạn viết thêm vào hàm setLabel như sau: |
|
... function setLabel(t, str) { t.label.text = ""; t.label2.text = ""; t.label3.text = ""; t.bar._visible = false;
var i = str.indexOf("/"); if(i > -1) { t.label2.text = str.substring(0, i); t.label3.text = str.substring(++i); t.bar._visible = true; return; }
t.label.text = str; } ... |
| Ở đầu hàm setLabel, ta viết các câu lệnh xóa sạch nội dung của các dòng chữ động label, label2, label3 trong ô đang xét. Vì hàm setLabel sẽ được gọi nhiều lần trong trò chơi, ta phải làm như vậy để bảo đảm lần gọi hàm setLabel trước không ảnh hưởng đến lần gọi hàm setLabel sau. Nếu không làm như vậy, nội dung còn sót lại của lần gọi hàm setLabel trước sẽ tạo ra kết quả "nhăng nhít" không mong đợi trong lần gọi hàm setLabel sau ở cùng một ô. Ngoài ra, câu lệnh t.bar._visible = false; dùng để tạm giấu đi thanh ngang trong ô. |
| Để biết chuỗi str được trao có phải là phân số hay không, ta gọi hàm indexOf("/") của chuỗi str. Nếu trong chuỗi str có ký tự "gạch chéo" (còn gọi là ký tự "xổ trái"), hàm indexOf("/") cho biết chỉ số của ký tự đó trong chuỗi str (mỗi ký tự trong một chuỗi ứng một chỉ số, ký tự đầu tiên ứng với chỉ số 0). Nếu trong chuỗi str không có ký tự "gạch chéo", hàm indexOf("/") trả về kết quả là -1. |
| Như vậy, khi kết quả trả về bởi hàm indexOf("/") của str lớn hơn -1, ta biết ngay đó là trường hợp phân số và cần có cách hiển thị thích hợp. "Hiển thị thích hợp" nghĩa là phần chuỗi con (substring) ở trước ký tự / phải được đưa vào label2 (tử số), phần chuỗi con ở sau ký tự / phải được đưa vào label3 (mẫu số). Để lấy chuỗi con trước ký tự /, ta gọi hàm substring(0, i) của str (lấy chuỗi con của str từ vị trí đầu tiên đến vị trí ngay trước i). Để lấy chuỗi con sau ký tự /, ta gọi hàm substring(++i) của str (lấy chuỗi con từ vị trí ngay sau i). Bạn chú ý, ta viết ++i để diễn đạt thao tác: tăng trị số của i thêm một đơn vị rồi lấy trị số của i. |
| Câu lệnh t.bar._visible = true; làm cho thanh ngang hiện ra chỉ trong trường hợp phân số. Chạy thử chương trình, bạn có kết quả như hình 4. |
![]() |
| Kết quả thu được dĩ nhiên chưa chấp nhận được vì chương trình của ta chưa phân biệt được hỗn số với phân số. Bạn để ý, đối với hỗn số, phần nguyên tách biệt với tử số bởi một ký tự trống. Nếu chuỗi str được trao cho hàm setLabel có chứa ký tự trống, ta biết ngay rằng đó là hỗn số và cần có cách hiển thị thích hợp. bạn chỉnh sửa hàm setLabel như sau: |
|
... function setLabel(t, str) { t.label.text = ""; t.label2.text = ""; t.label3.text = ""; t.bar._visible = false;
var i = str.indexOf(" "); if(i > -1) { t.label.text = str.substring(0, i); str = str.substring(++i); } i = str.indexOf("/"); if(i > -1) { t.label2.text = str.substring(0, i); t.label3.text = str.substring(++i); t.bar._visible = true; return; }
t.label.text = str; } ... |
| Theo nội dung mới của hàm setLabel, trước hết ta xem chuỗi str có chứa ký tự trống hay không bằng cách gọi hàm indexOf(" ") của str. Nếu có ký tự trống (kết quả trả về bởi hàm indexOf(" ") lớn hơn -1), ta lấy chuỗi con của str trước ký tự trống để đưa vào label, phần chuỗi con sau ký tự trống được gán vào str để đoạn mã tiếp theo xử lý. Chạy thử chương trình, bạn có kết quả như hình 5. |
![]() |
| Bạn thấy phần nguyên của hỗn số nằm ngay giữa ô vì dòng chữ động label của ô đang ở trạng thái "gióng giữa". Ta cần điều chỉnh thêm: trong trường hợp hỗn số, dòng chữ động label phải được chuyển qua trạng thái "gióng trái". Bạn viết thêm vào hàm setLabel như sau: |
|
... function setLabel(t, str) { t.label.text = ""; t.label2.text = ""; t.label3.text = ""; t.bar._visible = false;
var i = str.indexOf(" "); if(i > -1) { t.label.text = str.substring(0, i); str = str.substring(++i); var format = t.label.getTextFormat(); format.align = "left"; t.label.setTextFormat(format); } i = str.indexOf("/"); if(i > -1) { t.label2.text = str.substring(0, i); t.label3.text = str.substring(++i); t.bar._visible = true; return; }
t.label.text = str; } ... |
| Các câu lệnh vừa thêm nhằm lấy ra đối tượng quy định dạng thức (format) của dòng chữ động label, thay đổi thuộc tính align (gióng) của nó, từ "center" (gióng giữa) thành "left" (gióng trái). Sau đó, đối tượng quy định dạng thức được đặt trở lại vào label. Điều này tương tự như ta lấy một bộ phận của xe máy ra điều chỉnh và sau đó lắp trở lại vào xe. Các bài tiếp theo sẽ trình bày kỹ hơn về khái niệm đối tượng. |
| Cuối cùng, ta cần thực hiện một cải tiến nho nhỏ nữa. Một bạn đọc góp ý: "Tôi có đưa trò chơi của mình cho một vài em nhỏ chơi thử. Tôi quan sát và thấy các em không được phấn khích lắm. Đặc biệt là khi chọn sai quá 3 lần trò chơi lập tức khởi động lại làm cho các em không kịp nhận ra sự sai lầm của mình. Theo tôi, khi chọn sai quá 3 lần nên xuất hiện thông báo và vô hiệu hóa việc bấm chuột vào các ô tiếp theo. Như vậy người chơi có thời gian 'định thần' và nhận ra sai lầm của mình. Lúc đó muốn chơi lại thì bấm nút Reset". |
| Để thực hiện yêu cầu này, ở đầu hàm doPress (hàm xử lý tình huống onPress), ta cần kiểm tra ngay biến wrong. Nếu trị của biến wrong lớn hơn 3, ta cho hiển thị thông báo cần thiết và dùng câu lệnh return để thoát khỏi hàm, không làm gì nữa: |
|
... function doPress() { if(wrong > 3) { result.text = score + " Game over!"; return; } ...
/* if(wrong > 3) init(); */ if(testForWin()) result.text = score + " You've won!" else result.text = score; } ... |
| Bạn thấy rõ rằng việc kiểm tra biến wrong ở cuối hàm doPress không còn cần thiết nữa. |
| Dưới đây trình bày lại toàn bộ đoạn mã của của trò chơi để bạn tham khảo: |
|
nums = [ 2.2, 1.2, 3.65, 0.71, 0.31, 0.01, 3.25, 0.45, 2.25, 5.2, 2.5, 0.09, 0.65, 4.6, 0.37, 0.25, 3.75, 1.7, 0.48, 5.5 ]; exps = [ "2 1/5", "1 1/5", "3,65", "0,71", "0,31", "1/100", "3,25", "45/100", "2 1/4", "5 1/5", "2 1/2", "0,09", "0,65", "4,6", "0,37", "1/4", "3 3/4", "1,7", "0,48", "5 1/2" ]; tiles = new Array(); n = 0; for(i = 0; i < 4; i++) { tiles[i] = new Array(); for(j = 0; j < 4; j++) { attachMovie("Tile", "tile" + i + j, n++); tiles[i][j] = this["tile" + i + j]; tiles[i][j]._x = 100 * j; tiles[i][j]._y = 100 * i; tiles[i][j].onPress = doPress; } }
init(); reset.onPress = init;
function init() { score = 0; wrong = 0; for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { tiles[i][j]._visible = true; } } result.text = score; shuffleNumbers(); displayNumbers(); }
function doPress() { if(wrong > 3) { result.text = score + " Game over!"; return; }
tray = new Array(); for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { if(!tiles[i][j]._visible) continue; tray.push(tiles[i][j].num); } }
var min = tray[0]; for(i = 1; i < tray.length; i++) { min = Math.min(min, tray[i]); }
if(this.num == min) { score += 5; this._visible = false; } else { score -= 5; wrong++; }
if(testForWin()) result.text = score + " You've won!" else result.text = score; }
function testForWin() { for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { if(tiles[i][j]._visible) return false; } } return true; }
function displayNumbers() { var num, str; var k; for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { if(!tiles[i][j]._visible) continue;
k = getRandom(0, tray.length-1); num = tray[k]; tray.splice(k, 1);
str = getString(num); tiles[i][j].num = num; setLabel(tiles[i][j], str); } } }
function setLabel(t, str) { t.label.text = ""; t.label2.text = ""; t.label3.text = ""; t.bar._visible = false;
var i = str.indexOf(" "); if(i > -1) { t.label.text = str.substring(0, i); str = str.substring(++i); var format = t.label.getTextFormat(); format.align = "left"; t.label.setTextFormat(format); } i = str.indexOf("/"); if(i > -1) { t.label2.text = str.substring(0, i); t.label3.text = str.substring(++i); t.bar._visible = true; return; }
t.label.text = str; }
function getString(num) { for(var t = 0; t < nums.length; t++) { if(nums[t] == num) return exps[t]; } }
function shuffleNumbers() { tray = new Array(); for(i = 0; i < nums.length; i++) if(getRandom(0, 1)) tray.push(nums[i]); else tray.unshift(nums[i]); }
function getRandom(min, max) { return Math.floor(Math.random()*(max - min + 1) + min); } |
Đỗ Thanh Dương
Trần Thị Thúy Hằng @ 16:13 08/08/2012
Số lượt xem: 530
- Bài 46: Những cải tiến trước mắt (08/08/12)
- Bài 45: Giải thuật tìm số nhỏ nhất (08/08/12)
- Bài 44: Trình bày các số trong dãy (08/08/12)
- Bài 43: Trò chơi tìm số nhỏ nhất (08/08/12)
- Bài 42: Hiển thị câu chào mừng (08/08/12)






Các ý kiến mới nhất