0 votes

I'm making a scrabble rpg and the enemy should attack forming words as the player.
Lettertile.gd:

extends Node2D
class_name LetterTile

# Letter sprites by Kenney.nl
# https://opengameart.org/content/letter-tiles

var current_tile = 0
var letter_value = ""
var letter_points = 0
var box_position: Vector2
signal tile_clicked

func _ready():
    hide()
    randomize()
    $HiddenTimer.wait_time = rand_range(0.0, 0.5)
    $HiddenTimer.start()
    letter_value = Helpers.get_next_letter()
    current_tile = get_frame_from_letter(letter_value)
    letter_points = get_points_value(current_tile)
    $Tiles.frame = current_tile

func _on_ClickableArea_input_event(_viewport, event, _shape_idx):
    if event is InputEventMouseButton \
    and event.button_index == BUTTON_LEFT \
    and event.is_pressed():
        emit_signal("tile_clicked")

func _on_Clickable_pressed():
    emit_signal("tile_clicked")

func _on_HiddenTimer_timeout():
    show()

func get_points_value(letter):
    match letter:
        0:
            return 4
        1:
            return 1
        2:
            return 5
        3:
            return 2
        4:
            return 8
        5:
            return 10
        6:
            return 8
        7:
            return 3
        8:
            return 4
        9:
            return 3
        10:
            return 1
        11:
            return 3
        12:
            return 4
        13:
            return 1
        14:
            return 4
        15:
            return 1
        16:
            return 1
        17:
            return 1
        18:
            return 2
        19:
            return 1
        20:
            return 1
        21:
            return 3
        22:
            return 4
        23:
            return 1
        24:
            return 1
        25:
            return 10
        _:
            return 0

func get_frame_from_letter(letter):
    match letter:
        "Y":
            return 0
        "R":
            return 1
        "K":
            return 2
        "D":
            return 3
        "X":
            return 4
        "Q":
            return 5
        "J":
            return 6
        "C":
            return 7
        "W":
            return 8
        "P":
            return 9
        "I":
            return 10
        "B":
            return 11
        "V":
            return 12
        "O":
            return 13
        "H":
            return 14
        "A":
            return 15
        "U":
            return 16
        "N":
            return 17
        "G":
            return 18
        "E":
            return 19
        "T":
            return 20
        "M":
            return 21
        "F":
            return 22
        "L":
            return 23
        "S":
            return 24
        "Z":
            return 25
        "":
            return -1

func get_letter_value(tile_value):
    match tile_value:
        0:
            return "Y"
        1:
            return "R"
        2:
            return "K"
        3:
            return "D"
        4:
            return "X"
        5:
            return "Q"
        6:
            return "J"
        7:
            return "C"
        8:
            return "W"
        9:
            return "P"
        10:
            return "I"
        11:
            return "B"
        12:
            return "V"
        13:
            return "O"
        14:
            return "H"
        15:
            return "A"
        16:
            return "U"
        17:
            return "N"
        18:
            return "G"
        19:
            return "E"
        20:
            return "T"
        21:
            return "M"
        22:
            return "F"
        23:
            return "L"
        24:
            return "S"
        25:
            return "Z"
        _:
            return ""

Battle.gd:

func spawn_new_letters():
    for i in range(4):
        for j in range(4):
            var tile:LetterTile = Letter.instance()
            $LetterBox.add_child(tile)
            # warning-ignore:return_value_discarded
            tile.connect("tile_clicked", self, "on_tile_clicked", [tile])
            tile.position = Vector2(i * TILESIZE, j * TILESIZE) + Vector2(4 * i, 4 * j)
            tile.box_position = tile.position

func on_tile_clicked(tile:LetterTile):
    var start_pos = tile.global_position
    if tile.get_parent().name == "LetterBox":
        $LetterBox.remove_child(tile)
        $Word.add_child(tile)
        tile.global_position = start_pos
        current_word += tile.letter_value
        total_points += tile.letter_points
        if current_word.length() > 0:
            $UIButtons.show()
    else:
        var tile_position = tile.get_position_in_parent()
        $Word.remove_child(tile)
        $LetterBox.add_child(tile)
        $Tween.interpolate_property(tile,"position", start_pos - $LetterBox.position, tile.box_position,0.2,Tween.TRANS_LINEAR)
        current_word = current_word.substr(0, tile_position) + current_word.substr(tile_position + 1, current_word.length())
        total_points -= tile.letter_points
        if current_word.length() == 0:
            $UIButtons.hide()
    arrange_word_tiles()
    check_for_valid_word()
    set_hint_text()
    MusicPlayer.play_sfx("letter_click")

func clear_all_letters():
    for tile in $LetterBox.get_children():
        tile.queue_free()
    for tile in $Word.get_children():
        tile.queue_free()

func check_for_valid_word():
    if Helpers.is_word_valid(current_word):
        confirm_button.disabled = false
    else:
        confirm_button.disabled = true

...

in Engine by (122 points)
edited by

1 Answer

+1 vote
Best answer

For that, you'd first need an algorithm finding words from a dictionary matching your available letters, like the code below, which prints all words from my_dictionary matching the available letters:

var my_dictionary: PoolStringArray = [
    "about", "erase", "laser", "real", "really", "relax", "taxi"
]

func _ready():
    var available_letters: Array = ["Y", "A", "L", "S", "E", "R", "L", "X"]
    print(get_matching_words(available_letters))

func get_matching_words(available_letters: Array) -> Array:
    var result : Array = []
    for word in my_dictionary:
        var check_array : Array = available_letters.duplicate()
        var is_match : bool = true
        for letter in word.to_upper():
            var letter_pos = check_array.find(letter)
            if letter_pos == -1:
                is_match = false
                break
            else:
                check_array.remove(letter_pos)
        if is_match: result.append(word)
    return result

Your enemy could then use one of these words (a random one, the longest or the best one fitting to your scrabble board).

by (1,758 points)
selected by

I might suggest the addition of a break after a letter match fails as it'll make the search more efficient. So, this...

        if letter_pos == -1:
            is_match = false
            break # <---------- add this...

There's no sense in scanning the remaining letters in the current word once you know you can't create it...

Added, thanks. (Although I doubt that checking a couple of additional letters really causes any performance issues in a scrabble game ;-))

[edit] Thinking about it, with a dictionary containing thousands of words this break could indeed save a considerable amount of time. Thanks again! [/edit]

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.