Nghiên cứu & cải tiến giải thuật trong trò chơi di chuyển số trên bàn. (1 người xem)

  • Thread starter Thread starter SA_DQ
  • Ngày gửi Ngày gửi
Liên hệ QC

Người dùng đang xem chủ đề này

SA_DQ

/(hông là gì!
Thành viên danh dự
Tham gia
8/6/06
Bài viết
14,606
Được thích
22,927
Nghề nghiệp
U80
Bài I: PHẦN KHỞ ĐỘNG






Tại http:www.giaiphapexcel.comforumshowthread.php?414-Xin-gỏi-các-bạn-tham-khảo-trò-chơi-xếp-số-theo-1-trật-tự đang có file trò chơi dịch chuyển số đế xếp theo một trật tự nào đó mà bạn mong muốn hay định sẵn.
Trong hình là cảnh lúc mà ta mở file excel lên. Trên trang tính sẽ xuất hiền các ô chứa 40 số & 2 ô còn trống. Bên fía fải ta thấy thêm nút lệnh có tên là NEW. Nếu ta bấm vô nút này, các số trong bảng trên sẽ xáo trộn & cho ta 1 fương án khác (có nghĩa là chúng ta có thể bắt đầu với ván mới)

Cách thức chơi như sau

+ Di chuyển số: Hiện trên hình ta thấy 2 ô [G4] & [D9] đang trống;
Nếu ta dùng chuột bấm vô ô [H4] đang chứa trị 22 thì trị này lập tức sẽ nhảy sang ô trống bên trái liền kề & ô vừa bị ta bấm sẽ trở thành trống;

+ Xếp các số theo 1 trật tự đã định: Người chơi hoàn toàn có quyền định đoạt trật tự mà mình muốn về dẫy số tao thành,

DoiSo.GIF

Như tăng dần theo hàng & cột; như vậy [c4] sẽ là số 1, [D4] số 2. . . & [c5] - số 8 (hai ô trống sẽ ở hàng cuối
Hoặc tăng dần theo cột & hết cột 6 ô này đến cột 6 ô kết tiếp liền kề bên fải. (Cột cuối sẽ chứa 2 ô trống)

Cứ mỗi lần nhấn vô ô có nền sọc tím là 1 bước đi. Như vậy bạn cần chú í tránh bấm sai sót để khỏi bị trừ điểm oan. & nhiệm vụ của người chơi là dùng số bước đi tối thiểu để hoàn thành nhiệm vụ mà mình tự đề ra.

Lưu í là số bước đi của lần chơi mới (ván mới, khi ta nhấn vô nút ‘New’) được ghi lại trên ô [B3]
Nhưng bài này không fải nhằm quảng bá 1 trò chơi, mà chủ yếu là cùng nhau nghiên cứu giải fáp hiện tại để có thể cải tiến nó về mọi mặt, nhất là về mặt giải thuật.

Vì sao fải nêu vấn đề cải tiến; Vì rằng bài này đã có cách đây 4 năm; & trong quảng thời gian đó GPE.COM của chúng ta đã vươn vai & lớn mạnh không ngừng về tất cả các mặt, trong đó có mặt giải thuật.
Tác giả mong muốn giới thiệu kỹ giải thuật trò chơi này đến các bạn yêu thích VBA & hơn nữa sẽ nhận được các góp í cải tiến nhằm hoàn thiện trò chơi hơn trong tương lai.

I. Phần chuẩn bị hay gọi là giai đoạn khởi đầu)
1. Macro Auto_Open()


Khi bấm nút nào đó để mở file excel này lên, thì chúng ta đã cho các đòng lệnh dưới đây thực hiện
PHP:
 Dim StrC As String:     Global bRng As Range
Dim iZ As Integer:      Dim SoBuoc As Integer
 
Sub Auto_Open()
    Sheets("SaDQ").Select
    Set bRng = Range("C4:I9"):              Range("B3") = 0
End Sub
Mà trong đó 2 dòng lệnh đầu là ta khai báo các biến dùng chung trong toàn bộ chương trình. Tất cả các biến này đều có kiểu của nó, như StrC dùng để chứa chuỗi kí tự, iZ hay SoBuoc dùng để chứa số thuộc loại Integer, bRng là biến đối tượng, sẽ chứa 1 vùng các ô trên trang tính.
Tiếp theo là cho macro tự vận hành (Auto_Open) chạy. Như các bạn đã biết, một khi mở 1 file excel, thì chương trình sẽ tự đi kiếm macro nào có tên này & cho chạy tự động, nếu không có sự can thiếp từ bên ngoài.
Macro tự hành của chúng ta chỉ có 3 câu lệnh, đó là:
Kích hoạt trang tính, nhưng thật ra ở đây là lệnh thừa, vì chúng ta có mỗi 1 trang tính, chứ không nhiều nhặng gì cho cam.
Dòng thứ 2 là gán vùng cần thiết vô biến đã khai báo. (Các bạn tự tìm hiểu đó là vùng nào, nha)
Dòng 3: nạp số không cho [B3]; như trên ta biết, ô này sẽ thể hiện số bước mà chúng ta đã trãi qua trong 1 ván đấu.

2. Macro ván mới


Nội dung của macro này như dưới đây:
PHP:
 Sub VanMoi()
On Error Resume Next
 Dim SNg As Integer:                 Dim Schu As String
 Dim Rng As Range
 
1 Auto_Open
 StrC = "254026392738283729360001020304050607080900"
 StrC = StrC & "101112131415161718192021222324303531343233"
3 For iZ = 1 To 999
    Randomize:                          SNg = 1 + Int(36 * Rnd())
5    If SNg > 12 Then
        StrC = Mid(StrC, 5, 2 * SNg) & Left(StrC, 4) & Mid(StrC, 5 + 2 * SNg)
7    Else
       StrC = Mid(StrC, 2 * SNg + 1, 10) & Left(StrC, 2 * SNg) + Mid(StrC, 11 + 2 * SNg)
9    End If
 Next iZ
11 iZ = 1
 Application.ScreenUpdating = 0
13 For Each Rng In bRng
    Schu = Mid(StrC, 2 * iZ - 1, 2)
15    Rng.Value = Val(Schu):            If Schu = "00" Then Rng.Value = ""
17    iZ = 1 + iZ
 Next Rng
19 Application.ScreenUpdating = True
End Sub

Xin diễn dịch các câu lệnh theo tuần tự & mở rọng lang bang chút đến 1 số trong chúng:

Dòng đầu tiên (sau tên macro) để báo cho chương trình là hễ gặp lỗi nào đó bất kỳ, thì bạn cứ viêc bỏ qua & thực thi các lệnh còn lại cho đến hết.

Nhiệm vụ của chúng ta sau khi đọc bài này là sẽ tìm trong các câu lệnh dưới, câu nào có thể gây ra lỗi. Đây sẽ là câu hỏi khó, rất khó nữa là đằng khác nếu fải xét các macro không fải của mình viết hay đó là chương trình của mình nhưng đã quá lâu không rờ đến.

Tiếp theo là 2 dòng lệnh dùng để khai báo 3 biến sẽ cần xài. (& 3 biến lại có 3 kiểu khác nhau)

Dòng lệnh có số 1: Triệu gọi chạy macro tự hành đã nói bên trên.

Kế tiếp là các dòng lệnh gán các ký số tứ đến 40 vô biến kiểu chuỗi

Dòng lệnh có số 3: Tạo vòng lặp khoảng trên 990 lần (cho đến dòng lệnh số 10).

Mục đích của vòng lặp này là tạo ra 1 chuỗi chứa ký số trong biến StrC fân bố 1 cách hết sức ngẫu nhiên.

Để đạt mục tiêu này, tác giả đã:

Fần đầu của dòng lệnh 4: Fát động bộ tạo số ngẫu

Fần sau: tạo ra 1 số ngẫu nhiên dao động từ 1 cho tới 36. Thật ra con số 36 này ta có thể thay bằng những con số khác. Sau khi nghiên cứu xong vòng lặp này, chính các bạn đưa ra các con số có thể thay vô số 36 xem sao?

Dòng có số 5: Tạo ra điều kiện, mà nếu thỏa sẽ thực thi lệnh tại dòng số 6, bằng ngược lại – sẽ là dòng lệnh số 8

Thực ra 2 dòng lệnh 6 & 8 cùng có 1 kiểu là đem chuỗi chứa trong biến StrC chặt ra làm 3 khúc & ráp lại thành chuỗi mới với 1 trật tự ráp khác nhau mà thôi

Với dòng lệnh 6, ráp khúc giữa làm đầu, khúc đầu làm giữa; Với dòng còn lại: Ráp khúc cuối làm đầu, khúc giữa làm cuối.
Với gần ngàn lần lặp lại như vây & hầu như nhắm mắt băm & đảo sau khi băm thì ruột gan của StrC lộn tùng fèo cả lên chứ chả chơi!

Khi đọc các dòng lệnh trong vòng lặp này, ta chú í 1 điều rằng, tuy là nhắm mắt băm, nhưng băm vô vị trí lẻ không thôi đó nhe.

Các bạn có thể hòi vì sao fải vậy; Thưa rằng chúng ta đang xáo trộn 40 các số hợp lại trong 1 chuỗi, gồm rất nhiều số có 2 chữ số & 9 số có 1 chữ số đã được biến thành chuổi 2 ký số (Thêm ký số 0 đằng trước chúng). Nếu chúng ta để í kỹ hơn, chúng ta sẽ trong StrC có 2 cặp ‘00’. Đó là tiền đề để tạo ra 2 ô trống ở bước tiếp theo ngay sau đây.

Nhiệm vụ còn lại của macro này là tự động cắt khúc 2 ký số làm 1 & ráp vô các ô có trong biến bRng. Việc này được thực hiện bằng các lệnh từ dòng 13 cho đến dòng 18

Nhiệm vụ cùa dòng lệnh 12 & 19: Yêu cầu mi làm xong mới hãy báo cáo.

/(/hững câu hỏi có thể fát sinh sau khi đọc bài đầu này (ngoài 2 câu trên):

(*) Tại sau tác giả xài con số 999? Nhỏ hơn hay lớn hơn có vấn đề gì không?
(*) Tại sau lại cắt chuỗi trong StrC làm 3 khúc, hai hoặc bốn, hay trên nữa có được không?
(*) Hãy bằng tư duy trừu tượng bạn hãy fát biểu kết quả sau khi bấm 1 ô có số, khi mà hai ô trống nằm ngày trên & dưới liền kề với ô ta bấm; Cũng câu hỏi như vậy với các trường hợp các ô trống nằm trái & fải ô bấm.. . .
(*) Các thắc mắc khác từ nơi bạn xung quanh vấn đề này.

(còn tiếp)
 
Chỉnh sửa lần cuối bởi điều hành viên:
Bài II

MACRO ĐÁP ỨNG SỰ KIỆN ẤN NÚT 1 Ô SỐ
TRONG KHI CHƠI​


Khi ta tiến hành dùng chuột bấm vô 1 ô nào đó có chứa số kề bên ô trống để số đang ở ô ta vừa bấm chuyển sang ô này (& ô bấm sẽ trở thành ô trống) là ta cũng đồng thời fát lệnh chạy macro sự kiện có tại trang tính này.

Nội dung macro đó như sau:

PHP:
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 Dim Cls As Range
 Dim iRow As Integer, iCol As Integer
 
1 If Not Intersect(Target, bRng) Is Nothing Then
    For Each Cls In bRng
 3       With Cls
            iRow = Target.Row:              iCol = Target.Column
  5          iRow = iRow - Cls.Row:         iCol = iCol - Cls.Column
            If .Value = "" And KeNhau(iRow, iCol) Then
  7              .Value = Target.Value:      Target.Value = ""
                Exit For
  9          End If
        End With
11  Next Cls
    Range("B3").Value = 1 + Range("b3").Value
13 End If
 If Range("B3") > 2008 Then VanMoi
End Sub

& dưới đây là hàm tự tạo hỗ trợ macro làm việc chính xác & hiệu quả.
Hàm đó có nội dung khá đơn giản, như sau:

PHP:
Function KeNhau(iHang As Integer, iCot As Integer) As Boolean
 iHang = Abs(iHang):             iCot = Abs(iCot)
 
 If iHang = 0 And iCot = 1 Then KeNhau = -1
 If iCot = 0 And iHang = 1 Then KeNhau = -1
 If iCot = 0 And iHang = 0 Then KeNhau = -1
 
End Function

I./ Macro sự kiện

Trước tiên ta nghiên cứu sâu hơn xíu về nội dung các câu lệnh trong macro sự kiện, như sau:
Hai dòng lệnh khai báo các biến cần dùng nằm trên dòng lệnh số 1 ta đã quen biết từ bài trên. Có chăng chỉ là lấn cần kiểu loại của 2 biến Integer. . . .

Dòng lệnh 1 được diễn dịch như sau: Nếu (bạn- người dùng) vừa bấm chuột vô 1 ô nào đó thuộc các ô chứa trong biến bRng thì macro sẽ thực thi các lệnh nằm trên dòng lệnh có số 13.

Thực tế tác giả đã dùng fương thức Intersect() trong câu lệnh này. (Một số ít người vì thấy cú fáp của fương thức có 2 dấu ngoặt đơn nên nhầm tưởng đó là 1 hàm). & để hiểu rõ hơn về fương thức này ta có thể tìm ngay trên diễ đàn thân thương của chúng ta với từ khòa là ‘Intersect’

Trở lại với macro, dòng lệnh số 2 là tạo vòng lặp duyệt tất thẩy các ô chứa trong biến vùng bRng nêu trên. Vòng lặp này kết thúc khi gặp lệnh Next bên dưới.

Tiếp theo là các dòng lệnh bị kẹp giữa 2 dòng lệnh With. . . (Dg3) & End With. Cập lệnh này được hiểu nôm na là các câu lệnh bị kẹp giữa nó sẽ làm việc với đối tượng nêu trong dòng lệnh đầu của cấu trúc. Cụ thể đối tượng ở đây là biến vùng Cls.

Ví dụ
Mã:
 .Value = Target.Value

Thực ra các fần của dòng lệnh số 5 có thể viết gọn như vậy. Nhưng đấy là thể hiện cái ngây ngô của tác giả gần 4 năm về trước.

Các dòng lệnh số 4 & số 5 đều có thể viết gọn lại như ni:

PHP:
   iRow = Target.Row - .Row 
iCol  =  Target.Column  - .Column

& í nghĩa của nó được diễn dịch là:

Đem hiệu giữa chỉ số hàng của ô mà ta đụng đến (ô bị chuộc ta tác động) & chỉ số hàng của ô mà vòng lặp đang khảo sát gán vô biến (& tương tự như vậy với hiệu các chỉ số cột.)

Câu lệnh tại dòng 7 được hiểu là: Nếu trị trong ô đang khảo sát (bỡi vòng lặp [For. . . . Next[/b]) là rỗng (không chứa số trị) & trị trả về của hàm tự tạo KeNhau() là True thì macro sẽ thực hiện câu lệnh 7 (đó là câu lệnh hoán đổi trị giữa 2 ô.

Trước khi ta chuyển sang xét ý nghĩa & mục đích của hàm người dùng, ta xét luôn 2 câu lệnh dòng số 12 & 14

Dg12: Mang í nghĩa là: Trị trong ô B3 được tăng thêm 1; Ghi nhận ta đã ấn chuột 1 ô nào đó thuộc vùng chứa trong biến bRng.

Dg14: Nếu trị trong ô này lớn hơn 2008 thì nghỉ chơi ván đó! Do người chơi không biết đường hướng giải quyết bài toán đề ra.
Cũng cần nói thêm rằng Dg12 ta cũng có thể xài cách thức With. . . . End With, & xin đề nghị bạn đó!

II./ Hàm người dùng KeNhau

Để hàm người dùng làm việc không cự nự, ta fải cung cấp cho nó 2 số liệu kiểu dạng Integer. (Câu hỏi được nêu ra ở đây là ta có thể sửa lại hàm để cung cấp cho nó số liệu kiểu Byte, hay Long, thậm chí kiểu Double, hay Variant được không các bạn?

Xin thưa chỉ kiểu Byte là không ưng & Double & Variant là không nên & tốt nhất là kiểu Long – như các nhà lập trình chuyên nghiệp đã fân tích đâu đó trên diễn đàn.)

Hàm người dùng chỉ vỏn vẹn 4 dòng, trong đó
Dòng đầu lấy giá trị tuyệt đối của các tham biến đã truyền cho nó. Việc này nó nhờ hàm Asb() trong VBA; Hàm này cũng có trong Excel của chúng ta. Nhưng ta đang ở môi trường của VBA nên xài hàm của VBA để gây thiện cảm với hắn!

Để hiểu 3 dòng lệnh còn lại, cũng không thừa khi nhắc lại là ta trao cho hàm 2 trị là hiệu giữa 2 dòng & 2 cột của 2 ô, một ô là ta bấm chuột vô & ô còn lại là ô vòng lặp For . . . Next đang khảo sát tới.

Vậy thì 2 ô này kề nhau khi nào vậy các bạn?
Xin trả lời: Chỉ có thể có ba trường hợp sau:

(*) Cùng dòng & kề cột, iHang= 0 & hiệu tuyệt đối cột = 1

(*) Cùng cột, hơn kém nhau 1 dòng

(*) Cùng cột hay cùng hàng; Những ô này là những ô biên của vùng bRng (ta tạm gọi là bàn chơi kích thước 7 nhân 6 ô. Chứa 40 số & 2 ô trống)

Khi thỏa 1 trong ba trường hợp này, hàm tự tạo sẽ cho ta biết : Ừa, chúng kề nhau

III./ Sửa đổi cải tiến câu lệnh trong trò chơi

Xin nói trước, việc này là chuyện hoàn toàn của bạn, người đang muốn chinh fục những ngọn đồi & núi VBA chót vót.

Như ta đã thầy trong macro sự kiện, nếu ta ấn vô ô nào đó trong bàn chơi thì macro sẽ tạo vòng lập duyệt tất thẩy 42 ô của bàn. Cái nhiêu khê của chương trình đang là chuyện duyệt quá nhiều ô này, trong khi đáng ra chỉ duyệt 8 ô, thậm chí 4 ô (2 ô trên dưới & 2 ô trái fải) là tối đa. Một số ô khi ấn fải, ta chỉ nên duyệt có 3 ô hay 2 ô kề nó mà thôi.
Các bạn thử sức mình với hướng mới này xem có kết quả gì chăng?

Có vậy, ta cũng đỡ nhọc công với hàm người dùng nêu trên

Chúc các bạn sớm thành công trong tương lai.
 
Upvote 0
kính nể bác HYen17
thật công phu
 
Upvote 0
Web KT

Bài viết mới nhất

Back
Top Bottom