Spent some time cleaning up all the files that didn’t have devlogs or code to reship, and added a devlog to bf1easy. Had to figure out how to release things on github again lol
Log in to leave a comment
Putting on the final touches!
I got rid of all the debug stuff, cleaned up the app, built the apk for release, and created the demo video for those that have an ios phone.
Log in to leave a comment
I got rid of in app detection - snapchat stories/spotlight, youtube shorts, instagram reels, incognito mode, because it was genuinely causing me too much grief to keep up and maintain. I kept it in a separate branch so if i ever want to come back to it I can.
I also implemented fully blocking apps, so that they can be completely blocked within a time period. See: unblocked at: never (at this point just uninstall lol), and unblocked at a date in the future based on time based blocking.
Now that my features are basically mostly complete I think I can start rewriting the code to use more fragments than activities, because it is fully activity based right now.
Log in to leave a comment
bug busting: the quota was being inaccurate. it was showing the time remaining in the override when there were more generous options available (like additional time after the override) so i updated to make it show the most generous quota, while also picking the strictest block set out of the quota.
i fixed the time based rules not activating when they span an overnight period and made sure overnight rules extend into the next day, and I enabled allowing 0 quota so apps can be fully blocked.
finally put on the last polishing touches!
I set up brew and scoop as package managers so my project can easily be installed, and also added a demo mode so users can get started by downloading the sample zip problems to test it out!
Log in to leave a comment
i finished!
I used AI to help me get back on my feet and then I got custom input done, as well as preset test cases. I added quality of life features for it like ctrl+o to expand input/output, as well as checking for bits/stdc++.h in compilers and directing users to install a c++ compiler. I wasn’t going to bundle a compiler with it because it just doesn’t make sense.
Log in to leave a comment
I added time based rules! While time based rules are off, the blockset is always in effect. If time-based rules are on, the blockset is only active during the time period. I also went bug-busting and changed the interventions so that they aren’t preset length codes, but they are adjustable to any length depending on just how difficult the user needs the barrier to be.
Log in to leave a comment
Still working on recovering my stuff back. I wrote two helper python scripts to automate grabbing back the code submissions by scraping my dmoj submissions page and grabbing my raw code from DMOJ. I also had my devlog on a big training log file backed up to github, so I wrote a script to split the devlog file based on the # markdown headers and put it into the problems folder under {problem_code}.md. Now I’ll just grab a couple straggling CSES problems and I’m honestly just going to ignore the codeforces problems since theres so few of them.
Log in to leave a comment
My Macbook died, and I had initialized a git repository but I hadn’t pushed, so I ended up losing ALL of my problems and code. I’m out of a laptop for the next week and I’m using my desktop to get back on my feet. Luckily I kept all my devlogs backed up, and all my problem submissions are available online, but now I plan on writing a python script so I can repopulate my problems directory.
So I spent a good chunk of time rebuilding the whole go framework from scratch. I remembered the original layout of the files, but this was a really good lesson for me to remember to back my stuff up!!! I’m going to create a git repository and actually start pushing so I dont lose my stuff again.
Log in to leave a comment
I set up the text displays and got scrolling to work, using the up and down arrows to scroll. I considered using side to side scrolling but decided to use wordwrap to wrap text so text wouldn’t get cut off. I also got toasts to work, which display for 3 seconds and then return back to the previous page if an error occurs. The TUI remains responsive and the user can return back at anytime.
I worked on getting the C++ compiler to work and run test cases, as well as added test cases for each of my problems. Right now, it only displays a check if it passes, but I’m going to make it also show the input/output because there aren’t too many inputs for it to display.
Getting the compiler to work was a struggle. At first, I simply looked for g++, but g++ on mac doesn’t include the <bits/stdc++.h> header, which practically every competitive programming solution uses. I built a function to find any extra versions of g++ lying around such as g++-20, 17, etc. It also looks for c++ and other compiler names, and automatically attempts a retry if header files are missing.
Log in to leave a comment
Created a basic terminal UI which will look for all cpp files within problems and display them. i had to learn go for this, but it’s not too different from any other languages. walrus operator is interesting difference, and realising it autoinserts semicolons after every line is interesting. also no type definitions!
Log in to leave a comment
Last and final push!
CCC16S4 - applied interval DP to an S4 problem, basically solved it by myself! dp[l][r] is the biggest ball within the range. two cases: combining two balls, or combining three balls.
case 1: dp[l][k] + dp[k+1][r]
if dp[l][r] == sum[l, r] [l, r] is a ball
so check if both are ball and theyre equal and merge.
case 2: dp[l][a] + dp[a+1][b] + dp[b+1][r]
check if [l, a] is a ball, [b+1][r] is a ball, and if theyre equal, then merge.
just always track the maximum and output the maximum. answer is NOT dp[l][r]
cses 1202
minimum price is just normal dijkstra of N
but how to count how many paths?
run two dfs
one from N, one from 1.
an edge is only part of the final paths graph if dist_start[u] + cost + dis_end[v] == minimum cost
so build a directed acyclic graph off of that, and then run basic bfs keeping track of minimum distance and also maximum distance, as well as # of paths
also a bunch of other miscellaneous problems
Log in to leave a comment
Codeforces 1324F
Tree dp, looking for the best connected component. for each parent, only take the child if it actually helps, if it doesn’t, ignore.
CSES 1097
I figured it was interval DP, but I didn’t know how to interval dp or transition. I learned transition is sum[a, b] + dp[l][k] + dp[k+1][r]
dpn
same thing as above, classic template problem.
cses 1085
figured it was binary search, just printed m instead of l. learned to use basic l<=r, print l for the last false occurence, r for true
codeforces 760b
basic binary search. just implementation on check function. always make sure to have clear variables because collisions suck, make sure binary search bounds are reasonable
CCC22s5 round 2:
got 3/4 dp states on my own. afterwards i was able to derive transitions bymyself, even though it took some time.
ccc24s3
2 pointer approach, the order of both arrays has to be the same. conceptually figured out how each swipe works, direction, and ordering, but i reversed which direction to apply the sorts, kinda confusing variable names which led to me pointing things in the wrong direction, mixing beginning/end for swipes
ccc20s3
tried: prefix frequency and sliding window
apparently too slow because cache? anyways i learned how to hash
substring and hashing are expensive o(N) complexity
rubber duck debugging works!
codeforces 1201c
also bsearch and implementation of check function
cses 1163
learned to use lower_bound and decrement iterator to find first one less than k
cses 1632
greedy scheduling, learned to sort by ending time for scheduling.
Log in to leave a comment
did i not already do this?
oh its max dist to another node
okay its similar concept to tree distances 2
root the tree, find the longest distance (might be multiple branches)
dp[cur] = longest distances to a child inside one’s subtree
ans[root] = dp[root]
ans[child] is
ans[parent]
add 1 distance to the furthest child not in that child’s subtree
subtract 1 distance to the closes child in that child’s subtree
wait so ans[child] is
max(dp[child], dp[other children]+1)
Got everything above in 5 minutes.
okay 13 minutes in i realise that is not whats right
what i did was be on track to sovle distance to a leaf
okay new strat
2 bfs, find diameter
then one more bfs to find max dist
and then ans is just max of each one from either end of diameter
okay written in 20 minutes, time to debug
okay i forgot to input n-1 edges lol
solved in 22 minutes
what is the maximum difference between # of white and # black nodes
if i choose some subtree that contains the vertex v
maximize w-b
honestly this is like 2dfs. if the difference gets bigger as the tree goes up then it will dominate over all of its subtrees
so its monotonic - the answer can only ever increase as we go down the tree
count # of white and black vertices in each subtree, and track the diff in that subtree
got this part in 4 minutes
okay this question is kinda ridiculously worded (15 minutes in)
i dont know if the tree has to be rooted at 1
oh its just a connected component. doesn’t have to be a subtree.
so ans[nxt] is
original best value
plus ans[cur]
subtract any contribution to ans[cur] from the child so we dont double count
Log in to leave a comment
ccc22s3
first instinct:
max pitch M
good sample - a subsequence that has all unique numbers
im looking at this and it seems like dp to me? (10 minutes in)
when is it impossible?
k < n: impossible
m=2: k >
ill try subtask first
subtask 1: m=2
n=1
1
n=2
1 1 (2)
1 2 (3)
n=3
1 1 1 (3)
1 1 2 (4)
1 2 1 (5)
max = n*2-1
grabbed subtask 1 in 20 minutes
we can make k of form
n(n+1)/2 - (n-k)(n-k+1)/2
its greedy. essentially, at each position, you have a certain budget remaining. try to maximize the budget.
n=5, m=5, k=8
1 used: 1 budget: 4
1 2 used: 3 budget: 5
1 2 3 used: 6 budget: 6
1 2 3 3 used: 7 budget: 7
1 2 3 3 3 used: 8 budget: 8
n=5, m=5, k=9
1 u1b5
1 2 u3b6
1 2 3 u6b7
1 2 3 4 u10b8
1 2 3 1 u9b8
1 2 3 3 u7n8
1 2 3 3 1 u9b9
12, 23, 31
123
n=5 m=5 k=9
b5u1 1
b6u3 1 2
b7u6 1 2 3
b8u8 1 2 3 3
b9u9 1 2 3 3 1
building:
1 2 3 1 1
DONE:
Claude’s read on it:
Got on your own: The feasibility bounds (when to print -1), the range of valid K, the greedy intuition of “maximize budget at each step, leave room for the rest.”
Needed a push: The L_i framing — that total good samples = sum of longest-distinct-suffix lengths. You were thinking about it combinatorially (counting samples directly) instead of decomposing per-position.
Kept tripping on during implementation: Separating L values from pitches. You mixed them in the same array three times. The conceptual gap was clear (you understood the greedy) but translating “L_i says repeat the pitch L_i positions back” into clean code took too long.
Takeaway for Tuesday: When you see “count subarrays with property X,” immediately think per-endpoint contribution (how many valid subarrays end at each position). That’s the observation pattern you were missing. The implementation lesson: if your algorithm has two distinct concepts (L values vs pitches), use two distinct variables from the start.
Log in to leave a comment
coci23c1p3
first instinct (naive approach): run a loop across every possible table, every possible position
complexity: o(n^2m^2)
okay im learning sliding window max
this is 2d sliding window max. first compute the horizontal rows of each sliding window, then compute the vertical max using the horizontal row maxes.
sliding window max method:
for each row
head = 0, tail = -1
for each one in that row:
while(
head is less than tail ()
)
dont understand sliding window yet. come back later.
spent a long time learning sliding window dp.
Log in to leave a comment
coci13c1p4
first impression: dp? looks like knapsack dp.
does greedy work? sort by value, binary search bags
nlogn complexity
nice and easy multiset primer.
multiset for the binary search, already kept order, no dp required. just classic greedy.
Log in to leave a comment
CCC 19 S4
first instinct - maximizing and minimizing, dp?
dp[attraction][day], solution is dp[n][n/k+1]
working through samples: essentially if n%k == 0, the solution is already predetermined. nothing complicated required.
if n%k != 0, that means there is a block of length n%k we can play around with. we can shift this block around, inserting it between blocks of length 3 to figure out exactly which is most optimal
naive: shift each special block and then recompute
compute blocks of length k twice:once from beginning, once from end. store sum in psa.
then for each position of the special block, just sum up the left side, right side, and find the value for the middle block
okay so none of that was true - i was assuming blocks of length k and then one block of length n%k, but that’s just not true.
it’s still dp.
i spent too long on this so I just got spoonfed the dp transition - dp by endpoint
#days = ceil(n/k)
each day, the endpoint is in between [days*k + r-1, days * k + k-1] (0 indexed)
days*k - standard # of attractions in that day
+r-1 - minimum day length, subtract 1
+k-1 - maximum day length, subtract 1
okay i completely gave up and let AI spoonfeed me the dp transition because this was taking entirely too long. I need to move on.
Log in to leave a comment
ridiculous amount of time spent on this. almost a week. all the other tree dp problems were me trying to learn this problem
first impression: graph theory
kruskal + dp?
balance: highest degree compared to lowest cost
spoonfed: tree dp (n-1 edges)
if S is the set of paid students, every student that starts as N must be a neighbor to an S student
If we keep only paid nodes and edges b/t paid nodes, every component must have a Y within it.
dp:
base case: the child is already Y (nothing needed to be done)
if the child is N:
parent needs to be paid, or the grandchild needs to be paid
compare cost of getting paid from parent or child
for a given node:
A: is u paid?
B: does u already become Y without needing the parent? (either u was Y, or one of its child is paid)
C: if u is paid: does the paid component containing u already contain an initially Y node?
A no, C yes: not possible
#1 A yes, B yes, C yes: u is Y: valid | u is N: valid (paid, already has starter)
#2 A no, B yes, C no: u is Y: valid | u is N: valid (not paid, either influenced from below or is itself Y)
#3 A no, B no, C no u is Y: invalid | u is N:valid (not paid, not influenced from below (needs parent))
#5 A yes, B no, C no u is Y: invalid | u is N: valid (paid, needs parent)
Leaf base cases:
State 1: possible when u is Y, cost: C_u
State 2: possible when u is Y, cost: 0
State 3: possible when u is N, cost: 0
State 5: possible when u is N, cost: C_u
Throwing up the white flag: Need to learn: Tree DP
Learned: iterative DFS
Learned: solve integer overflow by setting INF to lower number (1e12)
Ended up just focusing on implementing the dp transition and being done.
Honestly I didn’t really get this problem, but I have a general sense of what to do. I’ve spent too much time stuck on this problem.
Log in to leave a comment
Start with leaf nodes for DP.
Two states to track: dp[node][0] is the max # of matched edges in that node’s subtree when that node is not matched with any of its children, dp[node][1] is when that node is matched with one of its children.
Leaf base case:
dp[leaf][0] = 0 - the subtree cannot have any matched edges
transition:
dp[par][0] = sum(max(dp[child][0], dp[child][1]))
// if this node does not match with any of its children, the max number of matchings is simply equal to the max number of matching in all of its children's subtrees
dp[par][1] = dp[par][0] + max of each child (dp[child][0] + 1 - max(dp[child][0], dp[child][1]))
// if we choose to pick one child to match with, we subtract that child's tree's contribution (-max(dp[child][0],dp[child][1])), and then we add that child's unmatched + 1
Approach: DFS greedy - leaves shouldn’t have a camera because then a camera would not be optimal. If a leaf has a parent then obviously putting the camera on the parent would cover the grandparent and its siblings too.
Therefore, use one DFS. Compute on the way up.
Log in to leave a comment
cses 1131
key insight: doing one bfs will find you one end of the diameter - it will find one of the furthest nodes.
afterwards, just run another bfs, that way you will find the other furthest node. the distance in between the two nodes is the tree diameter.
Log in to leave a comment
naive: brute force all 3 combinations (probably probably works for subtask 1 (n<=200))
how to do the geometry to figure out what point is in the middle? i have no clue
key insight: all three arc lengths must be less than half the circumference for (0, 0) to be in the triangle
key insight (spoonfed): instead of counting good triplets, subtract bad triplets
spoonfed: a triplet is bad if the 3 points all fit within some semicircle
sliding window strategy? since max circumference is 1e6, means we can count all in o(n) time, after nlogn sorting and using upper/lower bound
for sliding window, the upper bound may have to be a double (.5) if the circumference is odd
issue: overcounting
for a circle with points 0, 2, 5, 5, and circumference of 10
doubling the array would give 0, 2, 5, 5, 10, 12, 15, 15
sliding window <= C/2 would double count the 5 with the 10s
however we still need to cover the case of triangle with points 0, 2, 5 since this does not
2 big cases:
case 1: all points fit within window < C/2
bad triplets = all points within the window choose 2
case 2: all points fit within window <=C/2
rest = # of non beginning points
2a: bad triples = beginning * rest * end
2b: bad triplets = beginning choose 2 * end
2c: bad triples = end choose 2 * beginning
Log in to leave a comment
CSES 1674
Each node is a direct descendant of another node, other than 1, and the task is to compute how many total descendants each node has
graph theory, DP
base case:
leaf: 0
when building the adj graph, keep track of which nodes don’t have any descendants. these are the leaf nodes.
then just follow each one all the way up, adding each time (?)
should be o(N) time
dp recurrence is ans[v] = sum of descendants of each child + 1
add up the descendants of each child (which is also a descendant of v) and add the child too
topological order: store degree of each node, each time a node hits 0 incoming then you can push into the queue
i was going to include CCC22s5 because i gave up on that problem but it ended up being too long for adevlog
Log in to leave a comment
ccc23s4
Naive: For every road, eliminate it and then run dijkstra to find out if distance has increased
Pitfalls: how to find out which roads to cut? Cutting a road of cost 9 vs 2 roads of cost 5, etc.
Key insight gained (spoonfed): Kruskals
Works perfectly for subtask 1 where distance does not matter: just run normal kruskals
Next insight (also spoonfed): sort by distance + length
When processing an edge of length L, there are no other shorter edges of that length. That means the path to that point is already optimal.
It is impossible to “regret” not adding an edge because there is already a more optimal path to that node given the MST, and if the edge is guaranteed to be useless for any shortest path because if it does not shorten any path between the two nodes it connects, it will not shorten any path at all.
Not necessarily true. Therefore, because N and M are so small, we must run dijkstra to make sure this edge will shorten the distance b/t two edges
Log in to leave a comment
ccc24s4
Key insight: in any simple cycle, one edge will be grey, all other edges will alternate. This creates a tree.
Second problem: colouring edges is simple if the direction is monotonic: if grey edges only connect an ancestor to a descendant, we can simply colour paths based on depth. Otherwise having to go up and back down ends up with a path that does not alternate - at the turning point, there will be two paths of the same colour.
How to build this tree?
DFS tree ensures all unused edges connect an ancestor to a descendant. Then, we just colour based on parity during DFS.
Loop through all N to ensure all disconnected components are covered.
Log in to leave a comment
big addition! I added private tab tracking for most of the major browsers. i use string based detection, but i haven’t had any issues so far with the detection for the other app tabs like reels and stories. i might have to come back and fix it if it ends up bonking out though
Log in to leave a comment
refactoring refactoring refactoring!!
I spent some time cleaning up some of the code I’ve let AI write. There was a bunch of functions that were duplicated and spread out. I might need to go back and take a closer look at things later.
Log in to leave a comment
I added a lockdown feature to completely block all usage for a period of time.
Using this app for myself has been really helpful - I’m saving time for myself (that I can also put back into hacking!) and I’m also learning about how my brain gets around the barriers i put up. this app is really starting to take shape!
Log in to leave a comment
I created a working prototype - this is basically the minimum viable product. it takes a photo of a receipt and uses the openai api to read it, and then it puts it onto google drive and a spreadsheet.
Log in to leave a comment
I built a basic scaffolding UI for me to start and stop. I’ve enabled accessibilityservice and gotten a few automatic navigation taps to work but definitely still a work in progress so far
Log in to leave a comment
big update!
I added password protection to my override and settings screen, which has helped me stop granting myself extra time easily, but I can still access things when I really need to.
I also added youtube shorts tab detection so the app can now block all major short form video tabs now, while keeping access to everything else
Log in to leave a comment
I added instagram reels specific handling - that way I can keep in contact with my friends on chat but still block myself from scrolling!
Next step is to also add youtube shorts.
Log in to leave a comment
Been fighting a really annoying bug where the timer overlay doesn’t get cleaned up when leaving an app. It even ends up appearing on the lock screen and on the always on display..
I added Snapchat tab detection, because I found I would go to snapchat stories once I ran out of time on tiktok and instagram, but now my app can detect when I go into snapchat stories and block that, while still keeping access to my chats
I also added sorting, so I can find apps alphabetically or by how much time I spend on them
Log in to leave a comment
Fixing in app browsers made it so chrome wouldn’t get tracked even if it got blocked. I fixed that!
I also added decimal quotas, so I can now have quotas for 1.5 minutes or 10.25 minutes if I need.
Log in to leave a comment
I made a basic version of the bot! The bot can join a channel and play music using ytdl.
Couple of issues right now - the bot takes a very long time to join and play when the command is called. That needs to be fixed. I also need error handling for when users aren’t in a channel and disconnection handling.
Log in to leave a comment
Bug busting session: Fixed the timer not resetting when the window resets
Added overlay dragging and persistence of overlay position
Fixed the block screen sometimes display the wrong time and block set name
App’s core functionality is pretty much complete at this point! Just small things to smooth out.
Log in to leave a comment
The overlay kept bugging out, so I had to go through a debug session and add logs to figure out exactly what was going on. But now the overlay works very consistently.
I also added sorting by amount of time spent in the last week so users can choose the top apps they’re spending the most time on to block
Log in to leave a comment
I’ve created a working minimum viable product for my project!
I made the time reset based on the clock, so every 5 minutes will reset at 1:05, 1:10, etc.
At one point the save button didn’t work so I couldn’t save any changes to sets
I had to fix the tracking a couple of times, I tried using accessibility to track, and then realised usagestats was easier to use. But I have to query usagestats a lot because it doesn’t offer granular time frames.
The tracking overlay is frustrating. It keeps disappearing. It works perfectly in debug mode but I had to figure out how to only display when I’m in a block set app and not anywhere else. I’m trying to make it so I can drag the tracker but now I’m running into the same problem where it disappears again.
I tried writing tests to make sure things work and don’t break.
Log in to leave a comment