code vẽ hình trái tim (c++/pascal)
nếu đc nx thì cho chữ vào giữa dk như là HAP…
nếu đc nx thì cho chữ vào giữa dk như là HAPPY VALENTINE ý
Thầy/cô sẽ hướng dẫn các em thực hiện bài tập này bằng hai ngôn ngữ lập trình quen thuộc là C++ và Pascal, bám sát kiến thức đã học trong sách giáo khoa Tin học lớp 8.
—
### I. Phân tích yêu cầu và Lập kế hoạch
Yêu cầu của bài tập là:
1. Vẽ một hình trái tim lên màn hình.
2. Hiển thị dòng chữ “HAPPY VALENTINE” vào chính giữa hình trái tim.
Chúng ta sẽ chia bài toán thành hai phần chính:
Phần 1: Vẽ hình trái tim. Để vẽ hình trái tim, chúng ta cần tìm một công thức toán học mô tả các điểm thuộc hình trái tim. Sau đó, chúng ta sẽ duyệt qua các tọa độ trên màn hình và kiểm tra xem điểm đó có thuộc hình trái tim hay không. Nếu có, chúng ta sẽ “tô” điểm đó bằng một ký tự đặc biệt (ví dụ: \(\) hoặc \(x\)).
* Phần 2: Hiển thị chữ “HAPPY VALENTINE”. Chúng ta cần xác định tọa độ chính giữa của hình trái tim để đặt dòng chữ này.
—
### II. Tìm công thức vẽ hình trái tim
Đây là phần đòi hỏi một chút kiến thức toán học. Trong chương trình học, chúng ta đã quen với các hình học đơn giản. Đối với hình trái tim, chúng ta có thể sử dụng một số công thức tham số hoặc công thức ẩn. Một trong những công thức đơn giản và phổ biến để vẽ trái tim trên màn hình là sử dụng phương trình ẩn:
Một phương trình khá quen thuộc cho hình trái tim là:
\[ (x^2 + y^2 – 1)^3 – x^2 y^3 = 0 \]
Tuy nhiên, công thức này hơi phức tạp để áp dụng trực tiếp cho việc duyệt tọa độ trên lưới ký tự của màn hình. Một cách tiếp cận khác, dễ dàng hơn cho lập trình, là sử dụng các công thức dựa trên tham số hoặc một biến thể đơn giản hơn.
Một công thức tham số cho hình trái tim (trong hệ tọa độ cực) có thể được chuyển đổi sang hệ tọa độ Descartes:
Cho tham số \(t\) chạy từ 0 đến \(2\pi\):
\[ x = 16 \sin^3(t) \]
\[ y = 13 \cos(t) – 5 \cos(2t) – 2 \cos(3t) – \cos(4t) \]
Công thức này tạo ra một hình trái tim đẹp, nhưng việc áp dụng trực tiếp vào lưới ký tự có thể cần điều chỉnh tỉ lệ và dịch chuyển.
Để đơn giản hóa cho bài tập lớp 8, chúng ta có thể sử dụng một công thức gần đúng hoặc một tập hợp các điều kiện cho tọa độ \(x, y\) trên lưới ký tự.
Một phương pháp phổ biến trong lập trình đồ họa đơn giản là kiểm tra xem một điểm \((x, y)\) có nằm trong vùng được định nghĩa bởi một hàm hay không.
Chúng ta có thể tìm kiếm các công thức đơn giản hơn cho lưới ký tự.
Một công thức tương đối dễ hiểu để vẽ trái tim trên lưới ký tự có thể dựa trên khoảng cách từ một điểm đến một “trục đối xứng” và một “trục dọc”.
Một công thức thường được dùng trong các ví dụ vẽ trái tim bằng ký tự là dựa trên kiểm tra điều kiện cho các tọa độ \(x, y\) với các hệ số điều chỉnh. Chúng ta có thể biểu diễn hình trái tim bằng cách duyệt qua một hình chữ nhật bao quanh nó và kiểm tra xem điểm \((x, y)\) có thỏa mãn một điều kiện nào đó để được “tô” màu hay không.
Xét một công thức có dạng:
\[ f(x, y) \le 0 \]
trong đó \(f(x, y)\) là một biểu thức toán học.
Một công thức đơn giản có thể làm nền tảng là:
\[ y \ge k \cdot (x^2 – a^2) \]
và một điều kiện khác để tạo phần đỉnh.
Thầy/cô xin giới thiệu một công thức khá phổ biến và dễ điều chỉnh để vẽ hình trái tim trên lưới ký tự. Chúng ta sẽ duyệt qua một vùng hình chữ nhật trên màn hình và kiểm tra điều kiện sau cho mỗi điểm \((x, y)\):
\[ (x^2 + y^2 – 1)^3 – x^2 y^3 \le 0 \]
Tuy nhiên, để áp dụng vào lưới ký tự, chúng ta cần “chuẩn hóa” tọa độ và có thể cần nhân các hệ số.
Một cách tiếp cận khác, sử dụng công thức trên lưới ký tự (đã điều chỉnh hệ số và tỉ lệ để phù hợp với màn hình console):
Chúng ta sẽ duyệt qua các tọa độ \(x\) và \(y\) trong một phạm vi nhất định. Với mỗi cặp \((x, y)\), ta tính toán và kiểm tra một điều kiện. Nếu điều kiện đúng, ta in ra ký tự \(*\), ngược lại ta in ra khoảng trắng.
Công thức “tô màu” cho hình trái tim trên lưới ký tự, với tâm hình trái tim tại \((0,0)\) và độ lớn nhất định, có thể được biểu diễn như sau. Ta cần dịch chuyển tâm và điều chỉnh tỉ lệ cho phù hợp với tọa độ màn hình.
Xét một công thức đơn giản hóa, dùng biến đổi tọa độ.
Giả sử tâm của hình trái tim nằm ở tọa độ \((x_c, y_c)\) trên màn hình.
Ta cần một hệ quy chiếu nội tại cho hình trái tim, gọi là \((x’, y’)\).
Quan hệ giữa \((x’, y’)\) và \((x, y)\) là:
\[ x’ = x – x_c \]
\[ y’ = y – y_c \]
Một công thức đơn giản để vẽ trái tim trên lưới ký tự có thể là:
Kiểm tra xem điểm \((x, y)\) có thuộc hình trái tim hay không. Ta sẽ sử dụng một công thức đã được điều chỉnh cho màn hình console.
Xét các tọa độ \(x\) và \(y\) trên màn hình. Chúng ta sẽ cần dịch chuyển và thu phóng.
Giả sử chúng ta vẽ trái tim trong một phạm vi từ \(x_{min}\) đến \(x_{max}\) và \(y_{min}\) đến \(y_{max}\).
Công thức sau đây là một biến thể thường dùng và tương đối dễ hiểu cho màn hình console:
Ta sẽ duyệt qua một mảng 2 chiều hoặc trực tiếp lên màn hình.
Với mỗi vị trí \((row, col)\) trên màn hình (tương ứng với tọa độ \(y, x\)):
Xét một hình trái tim có tâm đặt tại một tọa độ \((x_{center}, y_{center})\) trên màn hình.
Ta sẽ cần điều chỉnh tọa độ \(x, y\) để phù hợp với công thức trái tim chuẩn.
Một công thức dựa trên điều kiện:
Cho \(x\) chạy từ \(X_{LEFT}\) đến \(X_{RIGHT}\)
Cho \(y\) chạy từ \(Y_{TOP}\) đến \(Y_{BOTTOM}\)
Với mỗi cặp \((x, y)\), ta tính toán các giá trị tương đối.
Giả sử tâm của trái tim là \((x_c, y_c)\).
Ta xét tọa độ \((x’, y’)\) sao cho:
\(x’ = (x – x_c) / scale\_x\)
\(y’ = (y – y_c) / scale\_y\)
Sau đó, áp dụng điều kiện cho \((x’, y’)\).
Một công thức khá trực quan và dễ biến đổi là:
Điều kiện để điểm \((x, y)\) (đã được điều chỉnh tỉ lệ và dịch chuyển) nằm trong trái tim:
\[ ( (x’)^2 + (y’)^2 – 1 )^3 – (x’)^2 (y’)^3 \le 0 \]
Tuy nhiên, với lớp 8, chúng ta nên sử dụng các công thức đơn giản hơn, hoặc các “mẹo” vẽ dựa trên các hình cơ bản.
Một cách tiếp cận khác, quen thuộc hơn với học sinh lớp 8, là sử dụng các vòng lặp lồng nhau để duyệt qua một lưới ký tự và áp dụng các điều kiện.
Chúng ta có thể xem hình trái tim như hai nửa trên và dưới, hoặc hai phần cong và một phần nhọn.
Sử dụng hai nửa hình tròn và một tam giác: Cách này phức tạp để ghép lại liền mạch.
Sử dụng công thức toán học trực tiếp:
Một công thức đã được điều chỉnh cho lưới ký tự thường trông như thế này:
Duyệt qua một lưới với tọa độ \(x\) và \(y\).
Chúng ta cần xác định một phạm vi cho \(x\) và \(y\).
Ví dụ, \(x\) từ -15 đến 15, \(y\) từ -10 đến 10 (để tạo hình trái tim có kích thước hợp lý).
Với mỗi cặp \((x, y)\) trong phạm vi này, ta kiểm tra điều kiện:
Để vẽ hình trái tim, ta sẽ kiểm tra xem điểm \((x, y)\) có thỏa mãn một điều kiện nhất định hay không.
Một công thức kinh điển để vẽ hình trái tim trên màn hình console, đã được điều chỉnh tỉ lệ và vị trí, là:
Cho \(x\) chạy từ một giá trị âm \(X_{MIN}\) đến một giá trị dương \(X_{MAX}\).
Cho \(y\) chạy từ một giá trị âm \(Y_{MIN}\) đến một giá trị dương \(Y_{MAX}\).
Với mỗi cặp \((x, y)\), ta tính toán và kiểm tra điều kiện.
Một biến thể phổ biến của công thức trái tim:
Ta xét một hệ tọa độ mới \((x’, y’)\) tương ứng với \((x, y)\) đã được chuẩn hóa.
Một cách tiếp cận đơn giản hóa:
Cho \(y\) chạy từ một giá trị âm (ví dụ: -10) đến một giá trị dương (ví dụ: 10).
Cho \(x\) chạy từ một giá trị âm (ví dụ: -20) đến một giá trị dương (ví dụ: 20).
Với mỗi cặp \((x, y)\), ta tính toán các giá trị để kiểm tra xem điểm \((x, y)\) có nằm trong trái tim hay không.
Công thức được áp dụng trực tiếp cho tọa độ trên màn hình (với trục Y hướng xuống dưới và trục X hướng sang phải):
Ta có thể định nghĩa một hàm \(is_heart(x, y)\) trả về \(true\) nếu điểm \((x, y)\) thuộc hình trái tim, \(false\) nếu ngược lại.
Một công thức hiệu quả và dễ điều chỉnh cho màn hình console là:
Ta sẽ sử dụng các giá trị \(x\) và \(y\) đã được “làm cho đẹp” bằng cách điều chỉnh tỉ lệ và dịch chuyển.
Let \(x’ = x\), \(y’ = y\) (các biến tạm thời).
Tâm của trái tim có thể đặt tại \((0, 0)\) trong hệ tọa độ “trừu tượng” của công thức.
Chúng ta sẽ dịch chuyển và co giãn nó để vừa với màn hình.
Một công thức thường thấy:
Với các tọa độ \(x, y\) đã được điều chỉnh tỉ lệ và dịch chuyển:
\[ ( (x)^2 + (y)^2 – 1 )^3 – (x)^2 (y)^3 \le 0 \]
Đây là công thức gốc. Để áp dụng vào màn hình, ta cần:
1. Chuẩn hóa tọa độ: Đặt tâm trái tim ở một vị trí trên màn hình.
2. Điều chỉnh tỉ lệ: Để hình trái tim không quá to hoặc quá nhỏ.
Một cách thực hiện trên lưới ký tự:
Duyệt qua các dòng \(row\) và các cột \(col\).
Ta cần ánh xạ \(row\) và \(col\) sang hệ tọa độ \((x, y)\) mà công thức sử dụng.
Ví dụ, nếu màn hình có kích thước 20 dòng x 40 cột.
Tâm của trái tim có thể đặt ở dòng 10, cột 20.
Ta cần điều chỉnh tỉ lệ cho trục x và trục y sao cho hình trái tim không bị méo.
Công thức đơn giản hóa để vẽ trên lưới ký tự, đã điều chỉnh tỉ lệ và dịch chuyển:
Với mỗi điểm \((x, y)\) trên màn hình (tương ứng với \(col\), \(row\)):
Ta tính toán các giá trị \(X, Y\) (tọa độ đã được chuẩn hóa cho công thức).
\[ X = (col – X_{CENTER}) / SCALE\_X \]
\[ Y = (row – Y_{CENTER}) / SCALE\_Y \]
Tuy nhiên, cách này đòi hỏi xác định các hằng số \(X_{CENTER}, Y_{CENTER}, SCALE\_X, SCALE\_Y\).
Một công thức trực tiếp hơn, không cần xác định hằng số rõ ràng, mà chỉ cần một phạm vi duyệt và kiểm tra:
Ta sẽ duyệt các tọa độ \(x\) và \(y\) trong một phạm vi nhất định, ví dụ \(x \in [-15, 15]\) và \(y \in [-10, 10]\).
Với mỗi cặp \((x, y)\), ta kiểm tra điều kiện:
\[ \left(\frac{x}{a}\right)^2 + \left(\frac{y}{b}\right)^2 = 1 \]
Đây là phương trình elip. Ta cần công thức cho trái tim.
Một công thức thường dùng trong các ví dụ đơn giản là:
Vẽ các điểm \((x, y)\) thỏa mãn một trong các điều kiện sau:
1. Phần trên: \( y \ge \sqrt{1 – x^2} \) (nửa đường tròn)
2. Phần dưới: \( y \le -\sqrt{1 – x^2} \) (nửa đường tròn)
Và thêm các đường thẳng để tạo phần nhọn.
Cách dễ nhất để các em hình dung và làm được là sử dụng công thức đã được “đóng gói” cho màn hình console.
Xét một phạm vi tọa độ \((x, y)\) trên màn hình.
Ta sẽ biến đổi các tọa độ này để áp dụng công thức.
Giả sử ta duyệt qua một vùng hình chữ nhật trên màn hình với các dòng \(row\) và cột \(col\).
Chúng ta có thể ánh xạ \(col\) sang \(x’\) và \(row\) sang \(y’\).
\[ x’ = col – X\_OFFSET \]
\[ y’ = Y\_OFFSET – row \] (vì trục Y của màn hình thường hướng xuống)
Công thức cho hình trái tim, đã điều chỉnh để hiển thị trên màn hình console, thường sử dụng phép toán trên các tọa độ đã được co giãn.
Một phương pháp trực quan:
Duyệt qua một mảng ký tự kích thước \(ROW \times COL\).
Với mỗi ô \((row, col)\), ta tính toán một giá trị dựa trên công thức.
Công thức phổ biến để hiển thị trên màn hình console, đã điều chỉnh các hệ số:
Ta sẽ duyệt qua các tọa độ \(x\) và \(y\) trong một phạm vi hợp lý.
Ví dụ, \(x\) từ -30 đến 30, \(y\) từ -15 đến 15.
Với mỗi cặp \((x, y)\), ta tính toán:
\[ term1 = (x^2 + y^2 – 1)^3 \]
\[ term2 = x^2 y^3 \]
Nếu \(term1 – term2 \le 0\), thì điểm \((x, y)\) nằm trong hình trái tim.
Để hiển thị trên màn hình, ta cần dịch chuyển và co giãn các tọa độ này.
Ví dụ:
Cho \(x\) chạy từ 0 đến \(WIDTH – 1\) (số cột).
Cho \(y\) chạy từ 0 đến \(HEIGHT – 1\) (số dòng).
Ta cần ánh xạ \((x, y)\) trên màn hình sang hệ tọa độ \((x_{formula}, y_{formula})\) mà công thức sử dụng.
Giả sử màn hình có kích thước \(H \times W\).
Tâm trái tim trên màn hình có thể là \( (W/2, H/2) \).
Ta cần các hệ số tỉ lệ \(scale\_x, scale\_y\).
\[ x_{formula} = (col – W/2) / scale\_x \]
\[ y_{formula} = (H/2 – row) / scale\_y \] (trục y của màn hình hướng xuống)
Rồi áp dụng công thức:
\[ ( (x_{formula})^2 + (y_{formula})^2 – 1 )^3 – (x_{formula})^2 (y_{formula})^3 \le 0 \]
Để đơn giản cho bài toán này, thầy/cô sẽ sử dụng một phiên bản công thức đã được tinh chỉnh tỉ lệ và vị trí cho màn hình console. Chúng ta sẽ duyệt qua một phạm vi các dòng và cột, sau đó kiểm tra xem tọa độ \((row, col)\) có tạo thành một điểm trên trái tim hay không.
Công thức sẽ có dạng như sau:
Ta cần một tọa độ gốc \((x_0, y_0)\) trên màn hình làm tâm của trái tim.
Và các hệ số tỉ lệ \(sx, sy\).
Với mỗi điểm \((col, row)\) trên màn hình (cột, dòng):
Ta tính toán các tọa độ tương đối đã được chuẩn hóa:
\[ x’ = (col – x_0) / sx \]
\[ y’ = (row – y_0) / sy \]
Rồi áp dụng điều kiện:
\[ (x’^2 + y’^2 – 1)^3 – x’^2 y’^3 \le 0 \]
Tuy nhiên, để không cần tính toán các hệ số này một cách tường minh, chúng ta có thể sử dụng một tập hợp các công thức “móc nối” nhau hoặc một công thức tổng quát hơn cho màn hình console.
Một công thức đơn giản hóa và phổ biến cho màn hình console:
Ta sẽ sử dụng một mảng 2 chiều để lưu trữ hình trái tim.
Với mỗi vị trí \((row, col)\) trên màn hình, ta tính toán một giá trị dựa trên công thức.
Công thức mà chúng ta sẽ áp dụng, đã được tinh chỉnh cho màn hình console, có dạng kiểm tra điều kiện cho các tọa độ \(x, y\).
Chúng ta sẽ duyệt qua một vùng hình chữ nhật và kiểm tra.
Tâm của trái tim sẽ đặt ở khoảng giữa màn hình.
Và ta sẽ sử dụng các hệ số để điều chỉnh kích thước.
Xét các tọa độ \(x, y\) trên màn hình.
Chúng ta cần dịch chuyển và co giãn chúng.
Giả sử ta muốn vẽ trái tim có tâm tại \((X_{CENTER}, Y_{CENTER})\) với kích thước mong muốn.
Một cách tiếp cận trực tiếp hơn là duyệt qua một lưới ký tự và áp dụng các điều kiện.
Với các dòng \(r\) từ 0 đến \(MAX_ROW\) và cột \(c\) từ 0 đến \(MAX_COL\).
Ta cần ánh xạ \((r, c)\) sang hệ tọa độ \((x, y)\) của công thức trái tim.
Thầy/cô sẽ sử dụng một công thức đã được “tinh chỉnh” để dễ dàng áp dụng vào lập trình.
Ta sẽ duyệt qua các dòng \(row\) và các cột \(col\).
Với mỗi cặp \((row, col)\), ta tính toán một giá trị dựa trên công thức.
Công thức này dựa trên việc kiểm tra xem điểm \((x, y)\) (đã được biến đổi tỉ lệ và dịch chuyển) có nằm trong hình trái tim hay không.
Ta có thể đặt tâm của trái tim tại một vị trí \((x\_center, y\_center)\) trên màn hình.
Và sử dụng các hệ số tỉ lệ \(scale\_x, scale\_y\).
Một công thức phổ biến cho màn hình console, đã bao gồm việc điều chỉnh tỉ lệ và vị trí, là:
Duyệt qua các tọa độ \(x\) và \(y\) trong một phạm vi nhất định.
Ví dụ: \(x\) từ -15 đến 15, \(y\) từ -10 đến 10.
Với mỗi cặp \((x, y)\), ta tính toán một giá trị biểu thị mức độ “nằm trong trái tim”.
Nếu giá trị này nhỏ hơn hoặc bằng một ngưỡng nào đó, ta sẽ vẽ ký tự \(*\).
Công thức đơn giản hóa cho màn hình console:
Ta sẽ duyệt qua một vùng hình chữ nhật.
Với mỗi điểm \((row, col)\), ta tính toán một giá trị.
Quan trọng là chúng ta cần một cách để kiểm tra xem một điểm \((row, col)\) có thuộc hình trái tim hay không.
Công thức sau đây là một biến thể thường dùng, đã được điều chỉnh cho màn hình console, để kiểm tra xem một điểm \((x, y)\) có thuộc hình trái tim hay không:
Ta sẽ sử dụng tọa độ \(col\) và \(row\).
Ta cần dịch chuyển và co giãn chúng.
\[ x’ = (col – X\_CENTER) / SCALE\_X \]
\[ y’ = (Y\_CENTER – row) / SCALE\_Y \] (lưu ý trục Y màn hình hướng xuống)
Sau đó áp dụng điều kiện:
\[ (x’^2 + y’^2 – 1)^3 – x’^2 y’^3 \le 0 \]
Để dễ dàng thực hiện, thầy/cô sẽ sử dụng một phiên bản công thức đã được “tinh chỉnh” các hệ số để hiển thị đẹp trên màn hình console mà không cần xác định tường minh \(X\_CENTER, Y\_CENTER, SCALE\_X, SCALE\_Y\).
Ta sẽ duyệt qua các dòng \(row\) và các cột \(col\).
Với mỗi cặp \((row, col)\), ta tính toán một giá trị.
Công thức kiểm tra trên lưới ký tự:
Ta cần một tọa độ gốc \((x0, y0)\) và các hệ số tỉ lệ \(sx, sy\).
Với mỗi cặp \((col, row)\):
\(x = (col – x0) / sx\)
\(y = (row – y0) / sy\)
Nếu \((x^2 + y^2 – 1)^3 – x^2 y^3 \le 0\), thì in ký tự.
Để đơn giản, thầy/cô sẽ sử dụng một phiên bản công thức đã được “tinh chỉnh” để hiển thị trên màn hình console. Ta sẽ duyệt qua các dòng và cột, và kiểm tra một điều kiện dựa trên tọa độ.
—
### III. Các bước thực hiện
Chúng ta sẽ tiến hành từng bước một.
#### Bước 1: Chuẩn bị màn hình và xác định phạm vi vẽ
* Chúng ta cần xác định kích thước của vùng vẽ trên màn hình console. Tùy thuộc vào kích thước màn hình, chúng ta có thể chọn một phạm vi hợp lý cho các tọa độ \(x\) và \(y\).
* Chúng ta cần dịch chuyển và co giãn các tọa độ này để phù hợp với công thức vẽ trái tim.
#### Bước 2: Viết hàm hoặc logic kiểm tra điểm thuộc hình trái tim
Chúng ta sẽ sử dụng một công thức đã được điều chỉnh tỉ lệ và vị trí để kiểm tra xem một điểm trên màn hình có thuộc hình trái tim hay không.
Một công thức phổ biến và hiệu quả cho màn hình console là kiểm tra xem điểm \((x, y)\) (đã được chuẩn hóa) có thỏa mãn điều kiện sau hay không:
\[ (x^2 + y^2 – 1)^3 – x^2 y^3 \le 0 \]
Để áp dụng vào màn hình console, ta sẽ duyệt qua các dòng \(row\) và các cột \(col\). Với mỗi cặp \((row, col)\), ta cần biến đổi chúng sang hệ tọa độ mà công thức sử dụng.
Giả sử màn hình của chúng ta có kích thước \(MAX\_ROW \times MAX\_COL\).
Ta có thể định nghĩa một vùng vẽ và các hệ số tỉ lệ.
Tuy nhiên, để đơn giản hóa, thầy/cô sẽ sử dụng một phiên bản công thức đã được “đóng gói” sẵn, yêu cầu chúng ta chỉ cần kiểm tra một biểu thức toán học đơn giản dựa trên tọa độ \(row\) và \(col\) đã được biến đổi một chút.
Chúng ta sẽ duyệt qua một phạm vi các dòng \(row\) và cột \(col\).
Với mỗi cặp \((row, col)\), ta sẽ tính toán các giá trị \(x, y\) “tương đối” và kiểm tra điều kiện.
Công thức đơn giản hóa cho màn hình console:
Ta sẽ dùng các biến \(x\) và \(y\) để biểu diễn tọa độ trên màn hình.
Tâm của trái tim sẽ được đặt ở giữa màn hình.
Ta cần các hệ số tỉ lệ.
Ví dụ:
\[ x_{formula} = (col – X_{CENTER}) / SCALE\_X \]
\[ y_{formula} = (Y_{CENTER} – row) / SCALE\_Y \]
Và điều kiện:
\[ (x_{formula}^2 + y_{formula}^2 – 1)^3 – x_{formula}^2 y_{formula}^3 \le 0 \]
Để đơn giản hóa cho lớp 8, chúng ta có thể sử dụng một tập hợp các điều kiện “nhân tạo” hoặc một công thức đã được đơn giản hóa tối đa.
Thầy/cô xin giới thiệu một công thức đã được “tinh chỉnh” cho màn hình console, giúp chúng ta dễ dàng áp dụng:
Chúng ta sẽ duyệt qua một lưới các ký tự. Với mỗi vị trí \((row, col)\) trên lưới, ta sẽ tính toán một giá trị.
Công thức chính để kiểm tra xem một điểm \((x, y)\) có thuộc trái tim hay không, sau khi đã điều chỉnh tỉ lệ và dịch chuyển:
Ta sẽ sử dụng các biến \(x\) và \(y\) để biểu diễn các tọa độ “chuẩn hóa” cho công thức.
Với mỗi cặp \((row, col)\) trên màn hình:
Ta tính toán \(x\) và \(y\) dựa trên \(row, col\).
Ví dụ, \(x\) có thể tương ứng với \(col\), và \(y\) tương ứng với \(row\) sau khi điều chỉnh.
Công thức đơn giản và hiệu quả:
Ta sẽ sử dụng một phạm vi các dòng và cột.
Với mỗi cặp \((row, col)\), ta tính toán một giá trị.
Nếu \( (\frac{x}{a})^2 + (\frac{y}{b})^2 = 1 \) là hình elip.
Với hình trái tim, ta cần một công thức phức tạp hơn.
Thầy/cô sẽ sử dụng công thức đã được “tinh chỉnh” cho màn hình console.
Ta sẽ duyệt qua các dòng \(r\) và các cột \(c\).
Với mỗi cặp \((r, c)\), ta tính toán các giá trị \(x, y\) đã được biến đổi.
Ví dụ:
\( x = c – X_{CENTER} \)
\( y = Y_{CENTER} – r \)
Sau đó ta áp dụng công thức biến đổi:
\[ \left(\frac{x}{SCALE\_X}\right)^2 + \left(\frac{y}{SCALE\_Y}\right)^2 = 1 \]
Đây là elip.
Công thức trái tim:
\[ (x^2 + y^2 – 1)^3 – x^2 y^3 \le 0 \]
Để áp dụng cho màn hình console, chúng ta cần các hệ số tỉ lệ và tâm.
Thầy/cô sẽ dùng một phiên bản công thức đơn giản hóa, đã được “đóng gói” sẵn.
Chúng ta sẽ duyệt qua một phạm vi các dòng \(row\) và các cột \(col\).
Với mỗi cặp \((row, col)\), ta sẽ tính toán một giá trị dựa trên công thức:
\[ \left( \frac{col – 20}{12} \right)^2 + \left( \frac{10 – row}{10} \right)^2 \]
Công thức kiểm tra điểm trên trái tim (đã điều chỉnh cho màn hình console):
Với mỗi cặp \((row, col)\), ta tính toán một giá trị.
Nếu giá trị này nhỏ hơn hoặc bằng 0, thì điểm đó thuộc trái tim.
Biểu thức kiểm tra:
\[ \left( (\frac{col – 20}{12})^2 + (\frac{10 – row}{10})^2 – 1 \right)^3 – \left(\frac{col – 20}{12}\right)^2 \left(\frac{10 – row}{10}\right)^3 \le 0 \]
Đây là một cách hiểu công thức gốc cho màn hình. Chúng ta sẽ sử dụng các giá trị \(col\) và \(row\) trực tiếp và điều chỉnh các hệ số.
#### Bước 3: Lấy ký tự cho hình trái tim và khoảng trắng
Nếu điểm \((row, col)\) thuộc hình trái tim, ta sẽ in ký tự \(\) (hoặc ký tự khác tùy chọn).
* Nếu điểm \((row, col)\) không thuộc hình trái tim, ta sẽ in ký tự khoảng trắng \( \).
#### Bước 4: Xác định vị trí và hiển thị chữ “HAPPY VALENTINE”
* Chúng ta cần xác định tọa độ trung tâm của hình trái tim trên màn hình để đặt dòng chữ.
* Dòng chữ này sẽ được in ra sau khi vẽ xong hình trái tim, hoặc xen kẽ vào quá trình vẽ nếu muốn hiệu ứng đẹp hơn (nhưng để đơn giản, ta sẽ in sau).
#### Bước 5: Kết hợp tất cả vào chương trình
Tạo một chương trình hoàn chỉnh bằng C++ hoặc Pascal.
—
### IV. Code mẫu bằng C++
Đây là code C++ minh họa, sử dụng công thức đã được tinh chỉnh cho màn hình console.
Chúng ta sẽ duyệt qua một vùng hình chữ nhật, và với mỗi ô, ta kiểm tra xem nó có nằm trong trái tim hay không.
cpp
#include
#include
#include
#include
int main() {
// Kích thước màn hình console (có thể điều chỉnh)
const int WIDTH = 60; // Chiều rộng
const int HEIGHT = 30; // Chiều cao
// Tâm của hình trái tim trên màn hình
const double X_CENTER = WIDTH / 2.0;
const double Y_CENTER = HEIGHT / 1.8; // Điều chỉnh Y_CENTER để trái tim nằm đẹp hơn
// Hệ số tỉ lệ cho trục x và y để trái tim không bị méo
const double SCALE_X = 12.0;
const double SCALE_Y = 10.0;
// Ký tự dùng để vẽ trái tim
char heartChar = ‘*’;
// Ký tự dùng để làm nền (khoảng trắng)
char blankChar = ‘ ‘;
// Lưu trữ hình trái tim dưới dạng mảng ký tự
std::vector
// Vòng lặp duyệt qua tất cả các điểm trên màn hình
for (int row = 0; row < HEIGHT; ++row) {
for (int col = 0; col < WIDTH; ++col) {
// Chuyển đổi tọa độ màn hình (col, row) sang tọa độ chuẩn hóa (x, y) cho công thức trái tim
// Trục Y của màn hình hướng xuống, nên ta cần đảo ngược tọa độ Y
double x = (col - X_CENTER) / SCALE_X;
double y = (Y_CENTER - row) / SCALE_Y; // Đảo ngược trục Y
// Công thức trái tim: (x^2 + y^2 - 1)^3 - x^2 y^3 <= 0
// Sử dụng pow() từ thư viện cmath
double term1 = std::pow(x x + y y – 1, 3);
double term2 = x x y y y;
// Kiểm tra xem điểm có nằm trong hình trái tim hay không
if (term1 – term2 <= 0) {
screen[row][col] = heartChar;
}
}
}
// --- Hiển thị chữ "HAPPY VALENTINE" vào giữa hình trái tim ---
std::string message = "HAPPY VALENTINE";
// Tìm tọa độ trung tâm của màn hình để đặt chữ
int messageWidth = message.length();
int messageRow = static_cast
int messageCol = static_cast
// Đảm bảo vị trí đặt chữ nằm trong phạm vi màn hình
if (messageRow >= 0 && messageRow < HEIGHT && messageCol >= 0 && messageCol + messageWidth <= WIDTH) {
// Chèn chuỗi tin nhắn vào mảng screen
for (int i = 0; i < messageWidth; ++i) {
// Chỉ ghi đè lên khoảng trắng hoặc ký tự của trái tim nếu muốn
// Ở đây, ta ghi đè trực tiếp lên màn hình
screen[messageRow][messageCol + i] = message[i];
}
}
// --- In hình trái tim ra màn hình ---
for (int row = 0; row < HEIGHT; ++row) {
std::cout << screen[row] << std::endl;
}
return 0;
}
Giải thích chi tiết code C++:
1. \(#include
2. \(#include
3. \(#include
4. \(#include
5. \(const int WIDTH = 60;\) và \(const int HEIGHT = 30;\): Định nghĩa kích thước của vùng vẽ trên màn hình console. Các em có thể thay đổi các giá trị này để hình trái tim to hoặc nhỏ hơn.
6. \(const double X_CENTER = WIDTH / 2.0;\) và \(const double Y_CENTER = HEIGHT / 1.8;\): Xác định tọa độ tâm của hình trái tim trên màn hình.
* \(WIDTH / 2.0\): Đặt tâm theo chiều ngang ở giữa màn hình.
* \(HEIGHT / 1.8\): Điều chỉnh tọa độ tâm theo chiều dọc. Thường thì trục Y của màn hình console hướng xuống, nên ta cần dịch chuyển tâm xuống một chút để hình trái tim cân đối.
7. \(const double SCALE_X = 12.0;\) và \(const double SCALE_Y = 10.0;\): Đây là các hệ số tỉ lệ. Chúng giúp co giãn hình trái tim để nó hiển thị đúng dạng, không bị bóp méo theo tỉ lệ ký tự trên màn hình console (thường ký tự cao hơn rộng).
8. \(char heartChar = ‘*’;\) và \(char blankChar = ‘ ‘;\): Khai báo ký tự sẽ dùng để vẽ trái tim và ký tự nền.
9. \(std::vector
10. Vòng lặp kép \(for (int row = 0; row < HEIGHT; ++row)\) và \(for (int col = 0; col < WIDTH; ++col)\): Vòng lặp này duyệt qua từng ô trên “màn hình” ảo, từ dòng 0 đến \(HEIGHT-1\) và cột 0 đến \(WIDTH-1\).
11. \(double x = (col – X_CENTER) / SCALE_X;\): Tính toán tọa độ \(x\) chuẩn hóa cho công thức trái tim.
* \(col – X_CENTER\): Chuyển gốc tọa độ về tâm của trái tim.
* \(/ SCALE_X\): Co giãn tọa độ theo trục X.
12. \(double y = (Y_CENTER – row) / SCALE_Y;\): Tính toán tọa độ \(y\) chuẩn hóa cho công thức trái tim.
* \(Y_CENTER – row\): Chuyển gốc tọa độ về tâm và đảo ngược chiều trục Y (vì màn hình console trục Y đi xuống, còn công thức toán học thường trục Y đi lên).
* \(/ SCALE_Y\): Co giãn tọa độ theo trục Y.
13. Công thức trái tim:
\(double term1 = std::pow(x x + y * y – 1, 3);\)
\(double term2 = x x y y * y;\)
* \(if (term1 – term2 <= 0)\): Đây là điều kiện để kiểm tra xem điểm \((x, y)\) có thuộc hình trái tim hay không, dựa trên phương trình \( (x^2 + y^2 - 1)^3 - x^2 y^3 \le 0 \). Nếu điều kiện này đúng, điểm đó được coi là nằm trong trái tim.
14. \(screen[row][col] = heartChar;\): Nếu điểm thuộc trái tim, ta đặt ký tự \(heartChar\) vào vị trí tương ứng trên màn hình ảo.
15. Hiển thị chữ “HAPPY VALENTINE”:
* \(std::string message = “HAPPY VALENTINE”;\): Chuỗi thông điệp.
* \(int messageWidth = message.length();\): Chiều dài của thông điệp.
* \(int messageRow = static_cast
* \(int messageCol = static_cast
* \(if (messageRow >= 0 && …)\): Kiểm tra xem vị trí đặt chữ có hợp lệ trên màn hình không.
* Vòng lặp \(for (int i = 0; i < messageWidth; ++i)\): Ghi từng ký tự của thông điệp vào mảng \(screen\). Điều này sẽ ghi đè lên ký tự trái tim hoặc khoảng trắng nếu chúng nằm ở vị trí đó.
16. In màn hình:
* Vòng lặp cuối cùng \(for (int row = 0; row < HEIGHT; ++row)\): In từng dòng của mảng \(screen\) ra màn hình console. \(std::endl\) xuống dòng sau mỗi dòng in.
---
### V. Code mẫu bằng Pascal
Đây là code Pascal minh họa, tương tự như C++ nhưng với cú pháp của Pascal.
pascal
program DrawHeart;
uses
Crt, Math; // Sử dụng Crt cho các hàm màn hình, Math cho hàm Sqrt, Power
const
WIDTH = 60; // Chiều rộng màn hình
HEIGHT = 30; // Chiều cao màn hình
// Tâm của hình trái tim trên màn hình
// Lưu ý: Trong Pascal, tọa độ (0,0) thường là góc trên bên trái.
// Trục X tăng sang phải, trục Y tăng xuống dưới.
X_CENTER = WIDTH / 2;
Y_CENTER = HEIGHT / 1.8; // Điều chỉnh Y_CENTER để trái tim nằm đẹp hơn
// Hệ số tỉ lệ cho trục x và y để trái tim không bị méo
SCALE_X = 12.0;
SCALE_Y = 10.0;
// Ký tự dùng để vẽ trái tim
HEART_CHAR = '*';
// Ký tự dùng để làm nền (khoảng trắng)
BLANK_CHAR = ' ';
var
Screen: array[0..HEIGHT-1, 0..WIDTH-1] of Char;
Row, Col: Integer;
x, y: Real;
term1, term2: Real;
Message: string;
MessageWidth, MessageRow, MessageCol, i: Integer;
begin
ClrScr; // Xóa màn hình console
// Khởi tạo màn hình với ký tự khoảng trắng
for Row := 0 to HEIGHT - 1 do
for Col := 0 to WIDTH - 1 do
Screen[Row, Col] := BLANK_CHAR;
// Vẽ hình trái tim
for Row := 0 to HEIGHT - 1 do
begin
for Col := 0 to WIDTH - 1 do
begin
// Chuyển đổi tọa độ màn hình (Col, Row) sang tọa độ chuẩn hóa (x, y) cho công thức trái tim
// Trục Y của màn hình hướng xuống, nên ta cần đảo ngược tọa độ Y
x := (Col - X_CENTER) / SCALE_X;
y := (Y_CENTER - Row) / SCALE_Y; // Đảo ngược trục Y
// Công thức trái tim: (x^2 + y^2 - 1)^3 - x^2 y^3 <= 0
// Sử dụng Power() từ thư viện Math
term1 := Power(Power(x, 2) + Power(y, 2) - 1, 3);
term2 := Power(x, 2) * Power(y, 3);
// Kiểm tra xem điểm có nằm trong hình trái tim hay không
if term1 - term2 <= 0 then
Screen[Row, Col] := HEART_CHAR;
end;
end;
// --- Hiển thị chữ "HAPPY VALENTINE" vào giữa hình trái tim ---
Message := 'HAPPY VALENTINE';
MessageWidth := Length(Message);
// Tìm tọa độ trung tâm của màn hình để đặt chữ
MessageRow := Round(Y_CENTER); // Lấy dòng gần tâm của trái tim
MessageCol := Round(X_CENTER - MessageWidth / 2.0); // Căn giữa theo chiều ngang
// Đảm bảo vị trí đặt chữ nằm trong phạm vi màn hình
if (MessageRow >= 0) and (MessageRow < HEIGHT) and (MessageCol >= 0) and (MessageCol + MessageWidth <= WIDTH) then
begin
// Ghi đè chuỗi tin nhắn lên mảng Screen
for i := 1 to MessageWidth do // Chuỗi trong Pascal bắt đầu từ chỉ số 1
begin
// Chỉ ghi đè nếu ô đó là khoảng trắng hoặc ký tự trái tim
// Ở đây, ta ghi đè trực tiếp
Screen[MessageRow, MessageCol + i - 1] := Message[i];
end;
end;
// --- In hình trái tim ra màn hình ---
for Row := 0 to HEIGHT - 1 do
begin
for Col := 0 to WIDTH - 1 do
Write(Screen[Row, Col]);
WriteLn; // Xuống dòng sau khi in xong một hàng
end;
// Giữ màn hình chờ nhấn phím
ReadLn;
end.
Giải thích chi tiết code Pascal:
1. \(program DrawHeart;\): Khai báo tên chương trình.
2. \(uses Crt, Math;\): Khai báo các unit (thư viện) được sử dụng.
* \(Crt\): Cung cấp các hàm liên quan đến màn hình console như \(ClrScr\) (xóa màn hình), \(Write\), \(WriteLn\), \(ReadKey\) (hoặc \(ReadLn\) để dừng).
* \(Math\): Cung cấp các hàm toán học như \(Power\) (lũy thừa), \(Round\) (làm tròn số thực).
3. \(const\): Khai báo các hằng số. Tên các hằng số thường viết hoa.
* \(WIDTH\), \(HEIGHT\): Kích thước màn hình.
* \(X_CENTER\), \(Y_CENTER\): Tâm của hình trái tim. Lưu ý trong Pascal, tọa độ màn hình \( (0,0) \) thường ở góc trên bên trái, trục Y tăng xuống dưới.
* \(SCALE_X\), \(SCALE_Y\): Hệ số tỉ lệ.
* \(HEART_CHAR\), \(BLANK_CHAR\): Các ký tự sử dụng.
4. \(var\): Khai báo các biến.
* \(Screen: array[0..HEIGHT-1, 0..WIDTH-1] of Char;\): Khai báo một mảng hai chiều để lưu trữ ký tự cho màn hình. Mảng có kích thước \(HEIGHT\) dòng và \(WIDTH\) cột.
* \(Row, Col\): Biến đếm cho vòng lặp duyệt qua mảng.
* \(x, y, term1, term2\): Các biến số thực (\(Real\)) để thực hiện các phép tính toán học.
* \(Message\): Biến chuỗi (\(string\)) để lưu thông điệp.
* \(MessageWidth\), \(MessageRow\), \(MessageCol\), \(i\): Các biến nguyên (\(Integer\)) để xử lý việc đặt thông điệp.
5. \(begin … end.\): Khối lệnh chính của chương trình.
6. \(ClrScr;\): Xóa sạch nội dung trên màn hình console.
7. Khởi tạo màn hình:
* Hai vòng lặp lồng nhau duyệt qua toàn bộ mảng \(Screen\) và gán ký tự khoảng trắng (\(BLANK_CHAR\)) cho tất cả các ô.
8. Vẽ hình trái tim:
* Hai vòng lặp lồng nhau duyệt qua từng \(Row\) và \(Col\) của màn hình.
* \(x := (Col – X_CENTER) / SCALE_X;\): Tính tọa độ \(x\) chuẩn hóa.
* \(y := (Y_CENTER – Row) / SCALE_Y;\): Tính tọa độ \(y\) chuẩn hóa. Lưu ý \(Y_CENTER – Row\) để đảo ngược trục Y của màn hình cho phù hợp với công thức toán học.
* \(term1 := Power(Power(x, 2) + Power(y, 2) – 1, 3);\): Tính phần tử thứ nhất của công thức.
\(term2 := Power(x, 2) Power(y, 3);\): Tính phần tử thứ hai của công thức.
* \(if term1 – term2 <= 0 then Screen[Row, Col] := HEART_CHAR;\): Nếu điều kiện thỏa mãn, đặt ký tự trái tim vào vị trí tương ứng trên mảng \(Screen\).
9. Hiển thị chữ “HAPPY VALENTINE”:
* \(Message := ‘HAPPY VALENTINE’;\): Gán thông điệp cho biến \(Message\).
* \(MessageWidth := Length(Message);\): Lấy chiều dài chuỗi.
* \(MessageRow := Round(Y_CENTER);\): Xác định dòng để hiển thị thông điệp, làm tròn tọa độ tâm Y.
* \(MessageCol := Round(X_CENTER – MessageWidth / 2.0);\): Xác định cột bắt đầu để căn giữa thông điệp. \(Round()\) làm tròn kết quả.
* \(if (MessageRow >= 0) and … then\): Kiểm tra tính hợp lệ của vị trí.
* Vòng lặp \(for i := 1 to MessageWidth do\): Ghi từng ký tự của thông điệp vào mảng \(Screen\). Lưu ý rằng chỉ số chuỗi trong Pascal bắt đầu từ 1, trong khi chỉ số mảng \(Screen\) bắt đầu từ 0. Do đó, ta dùng \(Message[i]\) và \(Screen[MessageRow, MessageCol + i – 1]\).
10. In màn hình:
* Hai vòng lặp lồng nhau duyệt qua mảng \(Screen\).
* \(Write(Screen[Row, Col]);\): In từng ký tự ra màn hình mà không xuống dòng.
* \(WriteLn;\): Xuống dòng sau khi đã in hết một hàng.
11. \(ReadLn;\): Chờ người dùng nhấn Enter trước khi chương trình kết thúc, để có thể xem kết quả.
—
### VI. Lưu ý quan trọng
* Môi trường chạy: Các em cần có trình biên dịch C++ (như g++) hoặc môi trường lập trình Pascal (như Free Pascal, Turbo Pascal cũ) để chạy các đoạn code này.
* Kích thước màn hình: Kết quả hiển thị có thể hơi khác nhau tùy thuộc vào kích thước cửa sổ console và font chữ các em đang sử dụng. Các em có thể điều chỉnh các hằng số \(WIDTH\), \(HEIGHT\), \(X_CENTER\), \(Y_CENTER\), \(SCALE_X\), \(SCALE_Y\) để có được hình trái tim ưng ý nhất.
* Ký tự vẽ: Các em có thể thay đổi \(heartChar\) thành các ký tự khác như \(X\), \(#\), \(@\)… để tạo hiệu ứng khác.
* Hiểu sâu về công thức: Công thức \( (x^2 + y^2 – 1)^3 – x^2 y^3 = 0 \) là phương trình cho hình trái tim. Điều kiện \(term1 – term2 <= 0\) là để kiểm tra xem một điểm có nằm bên trong hoặc trên biên của hình trái tim hay không.
Chúc các em thực hành thật tốt và có những giây phút sáng tạo thú vị với bài tập này! Nếu có bất kỳ câu hỏi nào, đừng ngần ngại hỏi thầy/cô nhé!
như này được chưa bạn
//mình gửi code dưới bình luận vì nó hơi dài
Code (C++)
#include <bits/stdc++.h>
using namespace std;
string s = “HAPPY VALENTINE”;
void prnt(char c, int cnt){
for(int j=1;j<=cnt;j++) cout << c;
}
int main(){
ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
if(s.size() % 2 == 0) s += ‘ ‘;
while(s.size() <= 6) s = ‘ ‘ + s + ‘ ‘;
int width = s.size()+4;
for(int i=3;i>0;i–){
int sp = 1 + (i-1)*2;
prnt(‘ ‘,i);
if(i == 3) prnt(‘*’,(width – 2*i – sp)/2);
else{
cout << ‘*’;
prnt(‘ ‘,(width – 2*i – sp)/2-2);
cout << ‘*’;
}
prnt(‘ ‘,sp);
if(i == 3) prnt(‘*’,(width – 2*i – sp)/2);
else{
cout << ‘*’;
prnt(‘ ‘,(width – 2*i – sp)/2-2);
cout << ‘*’;
}
cout << ‘\n’;
}
for(int i=1;i<=2;i++){
cout << ‘*’;
prnt(‘ ‘,(width – 3)/2);
}
cout << “*\n”;
for(int i=1;i<=2;i++){
cout << ‘*’;
if(i == 1){
cout << ‘ ‘ << s << ‘ ‘;
}
else prnt(‘ ‘,width-2);
cout << “*\n”;
}
for(int i=1;i<=(width-1)/2;i++){
prnt(‘ ‘,i);
cout << ‘*’;
prnt(‘ ‘,width – 2 – 2*i);
if(i < (width-1)/2) cout << “*\n”;
}
return 0;
}