modify files
This commit is contained in:
153
AIGrammar/View/WordPuzzleGameView.swift
Normal file
153
AIGrammar/View/WordPuzzleGameView.swift
Normal file
@ -0,0 +1,153 @@
|
||||
//
|
||||
// WordPuzzleView.swift
|
||||
// AIGrammar
|
||||
//
|
||||
// Created by oscar on 2025/7/16.
|
||||
//
|
||||
import SwiftUI
|
||||
import AVFoundation
|
||||
|
||||
struct WordPuzzleGameView: View {
|
||||
@State private var letters: [[String]] = []
|
||||
@State private var selectedLetters: [(Int, Int)] = []
|
||||
@State private var foundWords: [String] = []
|
||||
@State private var score: Int = 0
|
||||
@State private var remainingTime = 60
|
||||
@State private var isGameOver = false
|
||||
|
||||
@GestureState private var dragLocation: CGPoint? = nil
|
||||
@State private var audioPlayer: AVAudioPlayer?
|
||||
|
||||
let validWords = ["WORD", "GAME", "PUZZLE", "MET", "ME", "GO"]
|
||||
let gridSize = 4
|
||||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
|
||||
init() {
|
||||
var chars = Array("WORDPUZZLEGAMETO").map { String($0) }
|
||||
chars.shuffle()
|
||||
_letters = State(initialValue: stride(from: 0, to: chars.count, by: gridSize).map {
|
||||
Array(chars[$0..<$0+gridSize])
|
||||
})
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Time: \(remainingTime)s Score: \(score)")
|
||||
.font(.headline)
|
||||
|
||||
if isGameOver {
|
||||
Text("Game Over!")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
VStack(spacing: 4) {
|
||||
ForEach(0..<gridSize, id: \.self) { row in
|
||||
HStack(spacing: 4) {
|
||||
ForEach(0..<gridSize, id: \.self) { col in
|
||||
Text(letters[row][col])
|
||||
.font(.title)
|
||||
.frame(width: 50, height: 50)
|
||||
.background(
|
||||
selectedLetters.contains(where: { $0 == (row, col) }) ?
|
||||
Color.blue.opacity(0.7) : Color.gray.opacity(0.2)
|
||||
)
|
||||
.cornerRadius(8)
|
||||
.onTapGesture {
|
||||
handleSelect(row: row, col: col)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.gesture(
|
||||
DragGesture()
|
||||
.updating($dragLocation) { value, state, _ in
|
||||
state = value.location
|
||||
selectLetter(at: value.location)
|
||||
}
|
||||
.onEnded { _ in
|
||||
submitWord()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
ForEach(foundWords, id: \.self) { word in
|
||||
Text(word)
|
||||
.padding(6)
|
||||
.background(Color.green.opacity(0.3))
|
||||
.cornerRadius(6)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.onReceive(timer) { _ in
|
||||
guard !isGameOver else { return }
|
||||
if remainingTime > 0 {
|
||||
remainingTime -= 1
|
||||
} else {
|
||||
isGameOver = true
|
||||
timer.upstream.connect().cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 点击选择
|
||||
private func handleSelect(row: Int, col: Int) {
|
||||
if !selectedLetters.contains(where: { $0 == (row, col) }) {
|
||||
selectedLetters.append((row, col))
|
||||
}
|
||||
}
|
||||
|
||||
/// 拖动选择
|
||||
private func selectLetter(at point: CGPoint?) {
|
||||
guard let point = point else { return }
|
||||
for row in 0..<gridSize {
|
||||
for col in 0..<gridSize {
|
||||
let cellSize: CGFloat = 50 + 4
|
||||
let cellOrigin = CGPoint(x: CGFloat(col) * cellSize + 20,
|
||||
y: CGFloat(row) * cellSize + 120)
|
||||
let cellRect = CGRect(origin: cellOrigin, size: CGSize(width: 50, height: 50))
|
||||
if cellRect.contains(point) {
|
||||
if !selectedLetters.contains(where: { $0 == (row, col) }) {
|
||||
selectedLetters.append((row, col))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 提交选中的单词
|
||||
private func submitWord() {
|
||||
let word = selectedLetters.map { letters[$0.0][$0.1] }.joined()
|
||||
|
||||
if validWords.contains(word) && !foundWords.contains(word) {
|
||||
foundWords.append(word)
|
||||
score += word.count * 10
|
||||
playSound(named: "success")
|
||||
} else {
|
||||
playSound(named: "fail")
|
||||
}
|
||||
selectedLetters.removeAll()
|
||||
}
|
||||
|
||||
/// 播放提示音
|
||||
private func playSound(named name: String) {
|
||||
guard let url = Bundle.main.url(forResource: name, withExtension: "mp3") else { return }
|
||||
audioPlayer = try? AVAudioPlayer(contentsOf: url)
|
||||
audioPlayer?.play()
|
||||
}
|
||||
}
|
||||
|
||||
struct WordPuzzleGameView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
WordPuzzleGameView()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
WordPuzzleGameView()
|
||||
}
|
||||
Reference in New Issue
Block a user