"deepcopy" issues in Python - python-3.x

I was writing an AI solution to the TJ Wriggler problem and I'm having an issue that's causing my program to hang. I'm not exactly sure what it is since I should have more than enough memory to run the code. Rather, I think I'm changing a reference somewhere that I shouldn't be but I can't seem to figure out the exact place.
I'll run through execution up to the point where the program hangs:
First, the search function:
def BFTS(the_frontier):
index = 0
frontier = [the_frontier]
while(True):
if (not frontier):
return None
leaf = frontier.pop(0)
if (leaf.GoalTest()):
return leaf
index = index + 1
moves_list = leaf.FindMoves(index)
while(len(moves_list) > 0):
frontier.append(moves_list.pop(0))
This calls the FindMoves() function:
def FindMoves(self, current_index):
moves_list = []
for wriggler in self.State.Wrigglers:
for seg in [wriggler.Head(), wriggler.Tail()]:
is_head = {
wriggler.Head() : True,
wriggler.Tail() : False}[seg]
for move in self.State.FindEmptySpaces(seg):
if (move is not None):
moves_list.append(self.Move(wriggler.Index,
is_head, move['x'], move['y'], current_index))
return moves_list
FindEmptySpaces() finds all spaces that can be moved into and returns a list of dictionaries represent Cartesian coordinates. The FindMoves() function then calls the Move() function:
def Move(self, wriggler_id, is_head, new_x, new_y, current_index):
head_or_tail = {True : 0, False : 1}[is_head]
# Find action
new_action = ("%s %s %s %s" % (wriggler_id, head_or_tail, new_x, new_y))
new_node = self.CopyNode()
new_index = current_index
new_node.State.MoveWriggler(wriggler_id, new_y, new_x, is_head)
return Node(new_node.State, new_action, new_index, self.Cost + Node.Step_Cost)
This function calls the CopyNode() function:
def CopyNode(self):
# Create new Spaces List-of-List
new_spaces = DataStructures.CopyListOfLists(self.State.Spaces)
new_state = Board(new_spaces)
# Create new List of Actions
new_action = []
for action in self.Action:
new_action.append(action)
# Create new Node
return Node(new_state, new_action, self.Parent, self.Cost)
The CopyNode() function calls the CopyListOfLists() function:
def CopyListOfLists(the_list_of_list):
new_list = []
for lists in the_list_of_list:
temp_list = []
for items in lists:
temp_list.append(items)
new_list.append(temp_list)
return new_list
As far as I can tell, the program hangs in the function CopyNode() but the strange thing is that it only does this on the second pass through Search_BFTS(). As I say, I probably changed a reference somewhere but I'm not experienced enough in Python to know for sure if this is the actual problem.
Any help that could be offered would be great.
Thanks,

Related

How would I add other types of actions(status effects, heal, stat boosts, party switching) to a turn based system

I am currently trying to implement some different actions instead of attack, but I want to focus on a party function, that looks like this, I can figure out the types of actions I want but I want to change the battle function to fit into a role that can accept multiple actions and not just attack
func change_party(inventory,index): # Inventory being if enemy or player
if inventory[index].fainted == false: # Checks if the monster is not dead
var hold = inventory[0] # Holds the first slot and then applys it later to the selected slot
inventory[0] = inventory[index]
inventory[index] = hold
This changes the index that was inputed by buttons and swaps it around with the first slot in the array this is because the first slot is what monster is shown first, I also have this battle function:
func _battle():
participants = [player_inventory[0],enemy_inventory[0]]
participants.sort_custom(self, "check_speed")
for attacker in participants:
var player_move = player_inventory[0].move_slot[action_index]
var random_move = randi() % 3 + 1
var enemy_move = attacker.move_slot[0] # Use random_move currently 0
var target = _choose_target(attacker, participants)
if attacker == player_inventory[0]:
yield(attack(attacker, target, player_move), "completed")
else:
yield(attack(attacker, target, enemy_move), "completed")
if player_inventory[0].current_hp <= 0:
player_inventory[0].current_hp = 0
player_inventory[0].fainted = true
Battle_Ui_Updater()
self.current_state = GameState.LOST
return
if enemy_inventory[0].current_hp <= 0:
enemy_inventory[0].current_hp = 0
enemy_inventory[0].fainted = true
Battle_Ui_Updater()
self.current_state = GameState.WON
return
self.current_state = GameState.ACTION
One solution that came to me was trying to have all the actions in a array and just call the action I want based on input but I have no clue how I would make that look readable or bug-free
In this function is how it decides who´s turn it is based on speed, but sometimes I want the player to go first for example when I want to change party members and when the player has changed I want the enemy to start attacking, But I am scratching my head on how would I make it change actions, I know the attack function should be changed if I want it to do something else but I also want to be able to control who´s turn based on what type of action is used, I am sorry if I didnt explain it well, hope you guys understand, I don't want to repeat my self by making another similar to battle function so how do I avoid being repetitive while also doing what I want?
BattleScript:
func attack(attacker, target, move):
print(str(attacker.name) + " used " + str(move.name) + " " + str((move)))
var new_text = (attacker.name + " attacked with " + move.name)
text_scroller(new_text)
var data = recieve_damage(move,attacker,target)
#var data = target.take_damage(move,attacker) # Calls the function to take damage
yield(get_tree().create_timer(2), "timeout") #Wait for 2 seconds
Battle_Ui_Updater()
if data:
yield(critical_hit(attacker),"completed")
func critical_hit(attacker):
var new_text = (attacker.name + " has landed a critical hit! ")
text_scroller(new_text)
print(attacker.name + " has landed a critical hit!")
yield(get_tree().create_timer(2.5), "timeout")
func Get_effectiveness(attack_type,defence_type):
if attack_type == TypeData.types.none or defence_type == TypeData.types.none:
return 1
print("row : " + str(attack_type))
print("col : " + str(defence_type))
return TypeData.chart[attack_type][defence_type]
func recieve_damage(action,attacker,defender):
var critical = 1
var critical_chance = randi() % 100 + 1
if critical_chance <= 6.25:
critical = 2
var attack_mode
var defence_mode
if action.is_special == true:
attack_mode = attacker.special_attack
defence_mode = defender.special_defence
else:
attack_mode = attacker.attack
defence_mode = defender.defence
var type : float = Get_effectiveness(action.type,defender.type_1) * Get_effectiveness(action.type,defender.type_2)
var modifier : float = rand_range(0.85,1.0) * type * critical
var a : float = (2.0 * attacker.level / 5.0 + 2.0)
var b : float = (a * attack_mode * action.power / defence_mode) / 50.0
var c : float = (b + 2.0) * modifier
var damage = int(c)
defender.current_hp -= damage
print(str(attacker.name) + " = " + str(damage))
return critical > 1
Swap Party member with the current one:
func change_party(inventory,index):
if inventory[index].fainted == false:
var hold = inventory[0]
inventory[0] = inventory[index]
inventory[index] = hold
print(inventory[0].hp)
Battle_Ui_Updater()
move_ui_updater(player_inventory[0])
yield(get_tree().create_timer(2),"timeout")
I came up with this:
class_name Task
class Skipper:
signal skip
func emit() -> void:
emit_signal("skip")
var _instance:Object
var _method:String
var _parameters:Array
var _result = null
func _init(instance:Object, method:String, parameters:Array = []) -> void:
_instance = instance
_method = method
_parameters = parameters
func execute():
var instance = _instance
var parameters = _parameters
if instance != null and instance.has_method(_method):
_instance = null
_parameters = []
_result = instance.callv(_method, parameters)
if _result is GDScriptFunctionState && _result.is_valid():
_result = yield(_result, "completed")
return _result
var skipper = Skipper.new()
skipper.call_deferred("emit")
yield(skipper, "skip")
return _result
And this is how you initialize it:
var parameters = [player_inventory[0], enemy_inventory[0], player_inventory[0].move_slot[action_index]]
player_action = Task.new(self, "attack", parameters)
And this is how you use it:
yield(player_action.execute(), "completed")
The new thing about this class is that it will be asynchronous regardless of whether the method it calls is asynchronous or not (so you don't have to worry if what it calls yields or not). And it will complete after the method it calls completes, either way. And it even returns (null if what it calls does not return)!
Note: This code will memoize the result, and get rid of the parameters and instance it was linked to. Subsequent calls to execute will simply return the memoized result. This way it does not hold references unecesarily.
How do we do that?
Well, first of all, we are using callv to call a method by name with an array of parameters. There is a class FuncRef in Godot that can be used in similar fashion. However, not using it resulted more convinient.
Second, calling something asynchronous would be yield(..., "completed") but we don't know if what we are calling is asynchronous. So we check. How? If it is asynchronous, it actually returns the state it left the execution as a GDScriptFunctionState, so we check that.
If it is GDScriptFunctionState, then we can use yield(..., "completed") on it.
If it isn't. We need to somehow make the function asynchronous. We are going to do that by emitting a signal with call_deferred. I decided in making it an inner class so that it is not exposed outside of the script.
Also note that the code checks if the instance is not null and has the method passed. But if it does not, it will simply return the result it had stored. This is part of the memoization mechanism, however, it also means that you have no feedback if you passed the wrong instance of wrote the method name wrong.
Finally, it should work with this version of _battle:
func _battle():
participants = [player_inventory[0],enemy_inventory[0]]
participants.sort_custom(self, "check_speed")
for current_participant in participants:
if current_participant == player_inventory[0]:
yield(player_action.execute(), "completed")
else:
yield(enemy_action.execute(), "completed")
var state = decide_battle_state()
if state != GameState.BATTLE:
self.current_state = state
return
self.current_state = GameState.ACTION
func decide_battle_state():
if check_fainted(player_inventory[0]):
return GameState.LOST
if check_fainted(enemy_inventory[0]):
return GameState.WON
return GameState.BATTLE
func check_fainted(participant) -> bool:
if participant.current_hp > 0:
return false
participant.current_hp = 0
participant.fainted = true
Battle_Ui_Updater()
return true
You could make an Task with speed:
class_name BattleAction extends Task
# warning-ignore:unused_class_variable
var speed
func _init(instance:Object, method:String, parameters:Array = []).(instance, method, parameters) -> void:
pass
Then use like this:
var parameters = [player_inventory[0], enemy_inventory[0], player_inventory[0].move_slot[action_index]]
player_action = BattleAction.new(self, "attack", parameters)
player_action.speed = player_inventory[0].speed
And finally _battle can look like this:
With your BattleAction which has speed, you can do this:
func _battle():
actions = [player_action, enemy_action]
actions.sort_custom(self, "check_speed")
for action in actions:
yield(action.execute(), "completed")
var state = decide_battle_state()
if state != GameState.BATTLE:
self.current_state = state
return
self.current_state = GameState.ACTION

why Lists in a class were changed without my operate?

i run a class in a for cycle.
the code is:
graph_all = []
graph_now = graph_exc(-1)
for lin in res_str:
if lin.strip():
print(lin)
if lin[0] == 't' :
if graph_now.numbering != (-1) :
graph_all.append(graph_now)
graph_now = graph_exc(int(lin[4:-1]))
if lin[0] == 'v' :
graph_now.add_vertex(lin[2],lin[4])
if lin[0] == 'e' :
graph_now.add_edges(lin[2],lin[4],lin[6])
else:
continue
res_str is a list full of sentences.
I use graph_all to accommdate all the class.
when the first letter is 'v' or 'e',i put something in graph_now's lists.
when the first letter is 't',i put graph_now to graph_all and re-define the graph_all.but i find a problem:when i define graph_all again,the lists in class in graph_all will be nothing,too!
the code of graph_exc is :
class graph_exc:
edges_on_1 = []
edges_on_2 = []
edges_on_edges = []
vertex_on = {}
def __init__(self,numbering):
self.num_edge = 0
self.num_vertex = 0
self.numbering = numbering
self.edges_on_edges.clear()
self.edges_on_2.clear()
self.edges_on_1.clear()
self.vertex_on.clear()
def add_vertex(self,ver,ver_num):
self.vertex_on[ver] = ver_num
self.num_vertex += 1
def add_edges(self,point_1,edges,point_2):
self.edges_on_1.append(point_1)
self.edges_on_edges.append(edges)
self.edges_on_2.append(point_2)
self.num_edge += 1
the problem is : edges_on_1,edges_on_edges,edges_on_2 and vertex will be influenced.the data in here will disappear.but the num_edge and num_vertex are not.
I try to use deepcopy method to solve it.but it does not work.

Cannot find matching method - JIRA/Groovy

I am trying to calculate the amount of time an issue spent in a status. But experiencing some errors. The script below goes into the scripted field. Below is my script:
import com.atlassian.jira.component.ComponentAccessor
def changeHistoryManager = ComponentAccessor.changeHistoryManager
def currentStatusName = issue?.status?.name
def rt = [0L]
changeHistoryManager.getChangeItemsForField (issue, "status").reverse().each {item ->
def timeDiff = System.currentTimeMillis() - item.created.getTime()
if (item.fromString == currentStatusName) {
rt = -timeDiff
}
if (item.toString == currentStatusName){
rt = timeDiff
}
}
return (Math.round(rt.sum() / 3600000)) as Double
The error is in the last line of the script(the return statement).
I'm not sure what I'm doing wrong.
The errors I get are:
Static type checking - Cannot find matching java.lang.Object#sum() and Cannot find matching method java.lang.Match#round(java.lang.Object)
You are assigning rt to a Long in your two if blocks. (Just a long, not an array of longs.) Consequently there is no .sum() method available.
You could use
rt << -timeDiff
// or
rt << timeDiff
to add your timeDiffs to the array rather than redefining it.
You also could just initialize rt as 0 and then use rt += timeDiff or rt -= timeDiff if you prefer. It doesn't look like you really need that to exist as an array at all.
Example that may work for you:
import com.atlassian.jira.component.ComponentAccessor
def changeHistoryManager = ComponentAccessor.changeHistoryManager
def currentStatusName = issue?.status?.name
def rt = 0L
changeHistoryManager.getChangeItemsForField (issue, "status").reverse().each {item ->
def timeDiff = System.currentTimeMillis() - item.created.getTime()
if (item.fromString == currentStatusName) {
rt -= timeDiff
}
if (item.toString == currentStatusName){
rt += timeDiff
}
}
return rt / 3600000
// this could still be Math.round(rt/3600000) as Double if you need that; not sure what you're trying to do with the actual result

How can i get my list in correct order

I have almost got it right but my order will be like this
1.1.0.98
1.1.0.65
1.1.0.134
1.1.0.103
so it seems that when a third number is on it goes below the second.
code
def url = "http://mylink/".toURL().text
def root = new XmlSlurper().parseText(url)
def mylist = []
root.data.'content-item'.each{node ->
mylist << node.resourceURI.text() + node.relativePath.text().getAt(1..-2).replaceAll('/', '-').plus('.nupkg')
}
def result = []
mylist .reverseEach {
result << it
}
result
No idea what you're asking, but you can make your code a lot simpler...
This should work:
def result = root.data.'content-item'.collect { node ->
node.resourceURI.text() +
node.relativePath.text()[1..-2].replaceAll('/', '-') +
'.nupkg'
}.reverse()
no need for myList or reverseEach

Create comma separated string from 2 lists the groovy way

What I have so far is:
def imageColumns = ["products_image", "procuts_subimage1", "products_subimage2", "prodcuts_subimage3", "products_subimage4"]
def imageValues = ["1.jpg","2.jpg","3.jpg"]
def imageColumnsValues = []
// only care for columns with values
imageValues.eachWithIndex { image,i ->
imageColumnsValues << "${imageColumns[i]} = '${image}'"
}
println imageColumnValuePair.join(", ")
It works but I think it could be better. Wish there was a collectWithIndex ... Any suggestions?
There's no collectWithIndex, but you can achieve the same result with a little effort:
def imageColumns = ["products_image", "procuts_subimage1", "products_subimage2", "prodcuts_subimage3", "products_subimage4"]
def imageValues = ["1.jpg","2.jpg","3.jpg"]
def imageColumnsValues = [imageValues, 0..<imageValues.size()].transpose().collect { image, i ->
"${imageColumns[i]} = '${image}'"
}
println imageColumnsValues.join(", ")
This takes the list of items and a range of numbers from 0 size(list) - 1, and zips them together with transpose. Then you can just collect over that result.

Resources